xref: /openbsd-src/usr.sbin/relayd/pfe_route.c (revision 1577142d25857618a46a3cea398237378783141f)
1*1577142dSclaudio /*	$OpenBSD: pfe_route.c,v 1.14 2023/06/29 16:24:53 claudio Exp $	*/
234438db4Sreyk 
334438db4Sreyk /*
4d535e21cSreyk  * Copyright (c) 2009 - 2011 Reyk Floeter <reyk@openbsd.org>
534438db4Sreyk  *
634438db4Sreyk  * Permission to use, copy, modify, and distribute this software for any
734438db4Sreyk  * purpose with or without fee is hereby granted, provided that the above
834438db4Sreyk  * copyright notice and this permission notice appear in all copies.
934438db4Sreyk  *
1034438db4Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1134438db4Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1234438db4Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1334438db4Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1434438db4Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1534438db4Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1634438db4Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1734438db4Sreyk  */
1834438db4Sreyk 
1934438db4Sreyk #include <sys/types.h>
2034438db4Sreyk #include <sys/queue.h>
2134438db4Sreyk #include <sys/socket.h>
22137f9002Sclaudio #include <sys/uio.h>
2334438db4Sreyk 
2434438db4Sreyk #include <netinet/in.h>
2534438db4Sreyk #include <net/route.h>
26f04ff968Sreyk #include <arpa/inet.h>
2734438db4Sreyk 
28f04ff968Sreyk #include <limits.h>
29137f9002Sclaudio #include <stddef.h>
3034438db4Sreyk #include <stdio.h>
3134438db4Sreyk #include <unistd.h>
3234438db4Sreyk #include <string.h>
3334438db4Sreyk #include <errno.h>
3434438db4Sreyk 
3534438db4Sreyk #include "relayd.h"
3634438db4Sreyk 
3734438db4Sreyk void
init_routes(struct relayd * env)3834438db4Sreyk init_routes(struct relayd *env)
3934438db4Sreyk {
4034438db4Sreyk 	u_int	 rtfilter;
4134438db4Sreyk 
42586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDRT))
4334438db4Sreyk 		return;
4434438db4Sreyk 
4534438db4Sreyk 	if ((env->sc_rtsock = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
46efc39811Sbenno 		fatal("%s: failed to open routing socket", __func__);
4734438db4Sreyk 
4834438db4Sreyk 	rtfilter = ROUTE_FILTER(0);
4934438db4Sreyk 	if (setsockopt(env->sc_rtsock, AF_ROUTE, ROUTE_MSGFILTER,
5034438db4Sreyk 	    &rtfilter, sizeof(rtfilter)) == -1)
51efc39811Sbenno 		fatal("%s: ROUTE_MSGFILTER", __func__);
5234438db4Sreyk }
5334438db4Sreyk 
5434438db4Sreyk void
sync_routes(struct relayd * env,struct router * rt)5534438db4Sreyk sync_routes(struct relayd *env, struct router *rt)
5634438db4Sreyk {
5734438db4Sreyk 	struct netroute		*nr;
5834438db4Sreyk 	struct host		*host;
59e2318a52Sderaadt 	char			 buf[HOST_NAME_MAX+1];
6034438db4Sreyk 	struct ctl_netroute	 crt;
6134438db4Sreyk 
62586b5f8aSreyk 	if (!(env->sc_conf.flags & F_NEEDRT))
6334438db4Sreyk 		return;
6434438db4Sreyk 
6534438db4Sreyk 	TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry) {
6634438db4Sreyk 		print_host(&nr->nr_conf.ss, buf, sizeof(buf));
6734438db4Sreyk 		TAILQ_FOREACH(host, &rt->rt_gwtable->hosts, entry) {
6834438db4Sreyk 			if (host->up == HOST_UNKNOWN)
6934438db4Sreyk 				continue;
7034438db4Sreyk 
7185a8c65fSreyk 			log_debug("%s: "
7241835f22Sphessler 			    "router %s route %s/%d gateway %s %s priority %d",
7385a8c65fSreyk 			    __func__,
7434438db4Sreyk 			    rt->rt_conf.name, buf, nr->nr_conf.prefixlen,
7534438db4Sreyk 			    host->conf.name,
7641835f22Sphessler 			    HOST_ISUP(host->up) ? "up" : "down",
7741835f22Sphessler 			    host->conf.priority);
7834438db4Sreyk 
7934438db4Sreyk 			crt.up = host->up;
80a2195becSreyk 			memcpy(&crt.nr, &nr->nr_conf, sizeof(nr->nr_conf));
81a2195becSreyk 			memcpy(&crt.host, &host->conf, sizeof(host->conf));
82a2195becSreyk 			memcpy(&crt.rt, &rt->rt_conf, sizeof(rt->rt_conf));
8334438db4Sreyk 
84c2c37c5dSreyk 			proc_compose(env->sc_ps, PROC_PARENT,
85c2c37c5dSreyk 			    IMSG_RTMSG, &crt, sizeof(crt));
8634438db4Sreyk 		}
8734438db4Sreyk 	}
8834438db4Sreyk }
8934438db4Sreyk 
90137f9002Sclaudio static void
pfe_apply_prefixlen(struct sockaddr_storage * ss,int af,int len)91137f9002Sclaudio pfe_apply_prefixlen(struct sockaddr_storage *ss, int af, int len)
92137f9002Sclaudio {
93137f9002Sclaudio 	int q, r, off;
94137f9002Sclaudio 	uint8_t *b = (uint8_t *)ss;
95137f9002Sclaudio 
96137f9002Sclaudio 	q = len >> 3;
97137f9002Sclaudio 	r = len & 7;
98137f9002Sclaudio 
99137f9002Sclaudio 	bzero(ss, sizeof(*ss));
100137f9002Sclaudio 	ss->ss_family = af;
101137f9002Sclaudio 	switch (af) {
102137f9002Sclaudio 	case AF_INET:
103137f9002Sclaudio 		ss->ss_len = sizeof(struct sockaddr_in);
104137f9002Sclaudio 		off = offsetof(struct sockaddr_in, sin_addr);
105137f9002Sclaudio 		break;
106137f9002Sclaudio 	case AF_INET6:
107137f9002Sclaudio 		ss->ss_len = sizeof(struct sockaddr_in6);
108137f9002Sclaudio 		off = offsetof(struct sockaddr_in6, sin6_addr);
109137f9002Sclaudio 		break;
110137f9002Sclaudio 	default:
111137f9002Sclaudio 		fatal("%s: invalid address family", __func__);
112137f9002Sclaudio 	}
113137f9002Sclaudio 	if (q > 0)
114137f9002Sclaudio 		memset(b + off, 0xff, q);
115137f9002Sclaudio 	if (r > 0)
116137f9002Sclaudio 		b[off + q] = (0xff00 >> r) & 0xff;
117137f9002Sclaudio }
118137f9002Sclaudio 
119137f9002Sclaudio #define ROUNDUP(a) \
120137f9002Sclaudio 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
121137f9002Sclaudio 
12234438db4Sreyk int
pfe_route(struct relayd * env,struct ctl_netroute * crt)12334438db4Sreyk pfe_route(struct relayd *env, struct ctl_netroute *crt)
12434438db4Sreyk {
125137f9002Sclaudio 	struct iovec			 iov[5];
126137f9002Sclaudio 	struct rt_msghdr		 hdr;
127137f9002Sclaudio 	struct sockaddr_storage		 dst, gw, mask, label;
128137f9002Sclaudio 	struct sockaddr_rtlabel		*sr = (struct sockaddr_rtlabel *)&label;
129137f9002Sclaudio 	int				 iovcnt = 0;
13034438db4Sreyk 	char				*gwname;
13134438db4Sreyk 
132137f9002Sclaudio 	bzero(&hdr, sizeof(hdr));
133137f9002Sclaudio 	hdr.rtm_msglen = sizeof(hdr);
134137f9002Sclaudio 	hdr.rtm_version = RTM_VERSION;
135137f9002Sclaudio 	hdr.rtm_type = HOST_ISUP(crt->up) ? RTM_ADD : RTM_DELETE;
136137f9002Sclaudio 	hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY | RTF_MPATH;
137137f9002Sclaudio 	hdr.rtm_seq = env->sc_rtseq++;
138137f9002Sclaudio 	hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
139137f9002Sclaudio 	hdr.rtm_tableid = crt->rt.rtable;
140137f9002Sclaudio 	hdr.rtm_priority = crt->host.priority;
141137f9002Sclaudio 
142137f9002Sclaudio 	iov[iovcnt].iov_base = &hdr;
143137f9002Sclaudio 	iov[iovcnt++].iov_len = sizeof(hdr);
144137f9002Sclaudio 
145137f9002Sclaudio 	dst = crt->nr.ss;
146137f9002Sclaudio 	gw = crt->host.ss;
147a2195becSreyk 	gwname = crt->host.name;
148137f9002Sclaudio 	pfe_apply_prefixlen(&mask, dst.ss_family, crt->nr.prefixlen);
14934438db4Sreyk 
150137f9002Sclaudio 	iov[iovcnt].iov_base = &dst;
151137f9002Sclaudio 	iov[iovcnt++].iov_len = ROUNDUP(dst.ss_len);
152137f9002Sclaudio 	hdr.rtm_msglen += ROUNDUP(dst.ss_len);
15334438db4Sreyk 
154137f9002Sclaudio 	iov[iovcnt].iov_base = &gw;
155137f9002Sclaudio 	iov[iovcnt++].iov_len = ROUNDUP(gw.ss_len);
156137f9002Sclaudio 	hdr.rtm_msglen += ROUNDUP(gw.ss_len);
157137f9002Sclaudio 
158137f9002Sclaudio 	iov[iovcnt].iov_base = &mask;
159137f9002Sclaudio 	iov[iovcnt++].iov_len = ROUNDUP(mask.ss_len);
160137f9002Sclaudio 	hdr.rtm_msglen += ROUNDUP(mask.ss_len);
16134438db4Sreyk 
162a2195becSreyk 	if (strlen(crt->rt.label)) {
163137f9002Sclaudio 		sr->sr_len = sizeof(*sr);
164137f9002Sclaudio 		strlcpy(sr->sr_label, crt->rt.label, sizeof(sr->sr_label));
165137f9002Sclaudio 
166137f9002Sclaudio 		iov[iovcnt].iov_base = &label;
167137f9002Sclaudio 		iov[iovcnt++].iov_len = ROUNDUP(label.ss_len);
168137f9002Sclaudio 		hdr.rtm_msglen += ROUNDUP(label.ss_len);
169137f9002Sclaudio 		hdr.rtm_addrs |= RTA_LABEL;
17034438db4Sreyk 	}
17134438db4Sreyk 
17234438db4Sreyk  retry:
173137f9002Sclaudio 	if (writev(env->sc_rtsock, iov, iovcnt) == -1) {
17434438db4Sreyk 		switch (errno) {
17534438db4Sreyk 		case EEXIST:
17634438db4Sreyk 		case ESRCH:
177137f9002Sclaudio 			if (hdr.rtm_type == RTM_ADD) {
178137f9002Sclaudio 				hdr.rtm_type = RTM_CHANGE;
17934438db4Sreyk 				goto retry;
180137f9002Sclaudio 			} else if (hdr.rtm_type == RTM_DELETE) {
18134438db4Sreyk 				/* Ignore */
18234438db4Sreyk 				break;
18334438db4Sreyk 			}
18434438db4Sreyk 			/* FALLTHROUGH */
18534438db4Sreyk 		default:
18634438db4Sreyk 			goto bad;
18734438db4Sreyk 		}
18834438db4Sreyk 	}
18934438db4Sreyk 
19085a8c65fSreyk 	log_debug("%s: gateway %s %s", __func__, gwname,
19134438db4Sreyk 	    HOST_ISUP(crt->up) ? "added" : "deleted");
19234438db4Sreyk 
19334438db4Sreyk 	return (0);
19434438db4Sreyk 
19534438db4Sreyk  bad:
19685a8c65fSreyk 	log_debug("%s: failed to %s gateway %s: %d %s", __func__,
19734438db4Sreyk 	    HOST_ISUP(crt->up) ? "add" : "delete", gwname,
19834438db4Sreyk 	    errno, strerror(errno));
19934438db4Sreyk 
20034438db4Sreyk 	return (-1);
20134438db4Sreyk }
202