xref: /openbsd-src/usr.sbin/relayd/pfe_route.c (revision 1577142d25857618a46a3cea398237378783141f)
1 /*	$OpenBSD: pfe_route.c,v 1.14 2023/06/29 16:24:53 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 - 2011 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
23 
24 #include <netinet/in.h>
25 #include <net/route.h>
26 #include <arpa/inet.h>
27 
28 #include <limits.h>
29 #include <stddef.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 
35 #include "relayd.h"
36 
37 void
init_routes(struct relayd * env)38 init_routes(struct relayd *env)
39 {
40 	u_int	 rtfilter;
41 
42 	if (!(env->sc_conf.flags & F_NEEDRT))
43 		return;
44 
45 	if ((env->sc_rtsock = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
46 		fatal("%s: failed to open routing socket", __func__);
47 
48 	rtfilter = ROUTE_FILTER(0);
49 	if (setsockopt(env->sc_rtsock, AF_ROUTE, ROUTE_MSGFILTER,
50 	    &rtfilter, sizeof(rtfilter)) == -1)
51 		fatal("%s: ROUTE_MSGFILTER", __func__);
52 }
53 
54 void
sync_routes(struct relayd * env,struct router * rt)55 sync_routes(struct relayd *env, struct router *rt)
56 {
57 	struct netroute		*nr;
58 	struct host		*host;
59 	char			 buf[HOST_NAME_MAX+1];
60 	struct ctl_netroute	 crt;
61 
62 	if (!(env->sc_conf.flags & F_NEEDRT))
63 		return;
64 
65 	TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry) {
66 		print_host(&nr->nr_conf.ss, buf, sizeof(buf));
67 		TAILQ_FOREACH(host, &rt->rt_gwtable->hosts, entry) {
68 			if (host->up == HOST_UNKNOWN)
69 				continue;
70 
71 			log_debug("%s: "
72 			    "router %s route %s/%d gateway %s %s priority %d",
73 			    __func__,
74 			    rt->rt_conf.name, buf, nr->nr_conf.prefixlen,
75 			    host->conf.name,
76 			    HOST_ISUP(host->up) ? "up" : "down",
77 			    host->conf.priority);
78 
79 			crt.up = host->up;
80 			memcpy(&crt.nr, &nr->nr_conf, sizeof(nr->nr_conf));
81 			memcpy(&crt.host, &host->conf, sizeof(host->conf));
82 			memcpy(&crt.rt, &rt->rt_conf, sizeof(rt->rt_conf));
83 
84 			proc_compose(env->sc_ps, PROC_PARENT,
85 			    IMSG_RTMSG, &crt, sizeof(crt));
86 		}
87 	}
88 }
89 
90 static void
pfe_apply_prefixlen(struct sockaddr_storage * ss,int af,int len)91 pfe_apply_prefixlen(struct sockaddr_storage *ss, int af, int len)
92 {
93 	int q, r, off;
94 	uint8_t *b = (uint8_t *)ss;
95 
96 	q = len >> 3;
97 	r = len & 7;
98 
99 	bzero(ss, sizeof(*ss));
100 	ss->ss_family = af;
101 	switch (af) {
102 	case AF_INET:
103 		ss->ss_len = sizeof(struct sockaddr_in);
104 		off = offsetof(struct sockaddr_in, sin_addr);
105 		break;
106 	case AF_INET6:
107 		ss->ss_len = sizeof(struct sockaddr_in6);
108 		off = offsetof(struct sockaddr_in6, sin6_addr);
109 		break;
110 	default:
111 		fatal("%s: invalid address family", __func__);
112 	}
113 	if (q > 0)
114 		memset(b + off, 0xff, q);
115 	if (r > 0)
116 		b[off + q] = (0xff00 >> r) & 0xff;
117 }
118 
119 #define ROUNDUP(a) \
120 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
121 
122 int
pfe_route(struct relayd * env,struct ctl_netroute * crt)123 pfe_route(struct relayd *env, struct ctl_netroute *crt)
124 {
125 	struct iovec			 iov[5];
126 	struct rt_msghdr		 hdr;
127 	struct sockaddr_storage		 dst, gw, mask, label;
128 	struct sockaddr_rtlabel		*sr = (struct sockaddr_rtlabel *)&label;
129 	int				 iovcnt = 0;
130 	char				*gwname;
131 
132 	bzero(&hdr, sizeof(hdr));
133 	hdr.rtm_msglen = sizeof(hdr);
134 	hdr.rtm_version = RTM_VERSION;
135 	hdr.rtm_type = HOST_ISUP(crt->up) ? RTM_ADD : RTM_DELETE;
136 	hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY | RTF_MPATH;
137 	hdr.rtm_seq = env->sc_rtseq++;
138 	hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
139 	hdr.rtm_tableid = crt->rt.rtable;
140 	hdr.rtm_priority = crt->host.priority;
141 
142 	iov[iovcnt].iov_base = &hdr;
143 	iov[iovcnt++].iov_len = sizeof(hdr);
144 
145 	dst = crt->nr.ss;
146 	gw = crt->host.ss;
147 	gwname = crt->host.name;
148 	pfe_apply_prefixlen(&mask, dst.ss_family, crt->nr.prefixlen);
149 
150 	iov[iovcnt].iov_base = &dst;
151 	iov[iovcnt++].iov_len = ROUNDUP(dst.ss_len);
152 	hdr.rtm_msglen += ROUNDUP(dst.ss_len);
153 
154 	iov[iovcnt].iov_base = &gw;
155 	iov[iovcnt++].iov_len = ROUNDUP(gw.ss_len);
156 	hdr.rtm_msglen += ROUNDUP(gw.ss_len);
157 
158 	iov[iovcnt].iov_base = &mask;
159 	iov[iovcnt++].iov_len = ROUNDUP(mask.ss_len);
160 	hdr.rtm_msglen += ROUNDUP(mask.ss_len);
161 
162 	if (strlen(crt->rt.label)) {
163 		sr->sr_len = sizeof(*sr);
164 		strlcpy(sr->sr_label, crt->rt.label, sizeof(sr->sr_label));
165 
166 		iov[iovcnt].iov_base = &label;
167 		iov[iovcnt++].iov_len = ROUNDUP(label.ss_len);
168 		hdr.rtm_msglen += ROUNDUP(label.ss_len);
169 		hdr.rtm_addrs |= RTA_LABEL;
170 	}
171 
172  retry:
173 	if (writev(env->sc_rtsock, iov, iovcnt) == -1) {
174 		switch (errno) {
175 		case EEXIST:
176 		case ESRCH:
177 			if (hdr.rtm_type == RTM_ADD) {
178 				hdr.rtm_type = RTM_CHANGE;
179 				goto retry;
180 			} else if (hdr.rtm_type == RTM_DELETE) {
181 				/* Ignore */
182 				break;
183 			}
184 			/* FALLTHROUGH */
185 		default:
186 			goto bad;
187 		}
188 	}
189 
190 	log_debug("%s: gateway %s %s", __func__, gwname,
191 	    HOST_ISUP(crt->up) ? "added" : "deleted");
192 
193 	return (0);
194 
195  bad:
196 	log_debug("%s: failed to %s gateway %s: %d %s", __func__,
197 	    HOST_ISUP(crt->up) ? "add" : "delete", gwname,
198 	    errno, strerror(errno));
199 
200 	return (-1);
201 }
202