xref: /openbsd-src/usr.sbin/relayd/pfe_route.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: pfe_route.c,v 1.12 2017/05/28 10:39:15 benno 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 
23 #include <netinet/in.h>
24 #include <net/route.h>
25 #include <arpa/inet.h>
26 
27 #include <limits.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 
33 #include "relayd.h"
34 
35 struct relay_rtmsg {
36 	struct rt_msghdr	rm_hdr;
37 	union {
38 		struct {
39 			struct sockaddr_in	rm_dst;
40 			struct sockaddr_in	rm_gateway;
41 			struct sockaddr_in	rm_netmask;
42 			struct sockaddr_rtlabel	rm_label;
43 		}		 u4;
44 		struct {
45 			struct sockaddr_in6	rm_dst;
46 			struct sockaddr_in6	rm_gateway;
47 			struct sockaddr_in6	rm_netmask;
48 			struct sockaddr_rtlabel	rm_label;
49 		}		 u6;
50 	}			 rm_u;
51 };
52 
53 void
54 init_routes(struct relayd *env)
55 {
56 	u_int	 rtfilter;
57 
58 	if (!(env->sc_conf.flags & F_NEEDRT))
59 		return;
60 
61 	if ((env->sc_rtsock = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
62 		fatal("%s: failed to open routing socket", __func__);
63 
64 	rtfilter = ROUTE_FILTER(0);
65 	if (setsockopt(env->sc_rtsock, AF_ROUTE, ROUTE_MSGFILTER,
66 	    &rtfilter, sizeof(rtfilter)) == -1)
67 		fatal("%s: ROUTE_MSGFILTER", __func__);
68 }
69 
70 void
71 sync_routes(struct relayd *env, struct router *rt)
72 {
73 	struct netroute		*nr;
74 	struct host		*host;
75 	char			 buf[HOST_NAME_MAX+1];
76 	struct ctl_netroute	 crt;
77 
78 	if (!(env->sc_conf.flags & F_NEEDRT))
79 		return;
80 
81 	TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry) {
82 		print_host(&nr->nr_conf.ss, buf, sizeof(buf));
83 		TAILQ_FOREACH(host, &rt->rt_gwtable->hosts, entry) {
84 			if (host->up == HOST_UNKNOWN)
85 				continue;
86 
87 			log_debug("%s: "
88 			    "router %s route %s/%d gateway %s %s priority %d",
89 			    __func__,
90 			    rt->rt_conf.name, buf, nr->nr_conf.prefixlen,
91 			    host->conf.name,
92 			    HOST_ISUP(host->up) ? "up" : "down",
93 			    host->conf.priority);
94 
95 			crt.up = host->up;
96 			memcpy(&crt.nr, &nr->nr_conf, sizeof(nr->nr_conf));
97 			memcpy(&crt.host, &host->conf, sizeof(host->conf));
98 			memcpy(&crt.rt, &rt->rt_conf, sizeof(rt->rt_conf));
99 
100 			proc_compose(env->sc_ps, PROC_PARENT,
101 			    IMSG_RTMSG, &crt, sizeof(crt));
102 		}
103 	}
104 }
105 
106 int
107 pfe_route(struct relayd *env, struct ctl_netroute *crt)
108 {
109 	struct relay_rtmsg		 rm;
110 	struct sockaddr_rtlabel		 sr;
111 	struct sockaddr_storage		*gw;
112 	struct sockaddr_in		*s4;
113 	struct sockaddr_in6		*s6;
114 	size_t				 len = 0;
115 	char				*gwname;
116 	int				 i = 0;
117 
118 	gw = &crt->host.ss;
119 	gwname = crt->host.name;
120 
121 	bzero(&rm, sizeof(rm));
122 	bzero(&sr, sizeof(sr));
123 
124 	rm.rm_hdr.rtm_msglen = len;
125 	rm.rm_hdr.rtm_version = RTM_VERSION;
126 	rm.rm_hdr.rtm_type = HOST_ISUP(crt->up) ? RTM_ADD : RTM_DELETE;
127 	rm.rm_hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY | RTF_MPATH;
128 	rm.rm_hdr.rtm_seq = env->sc_rtseq++;
129 	rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
130 	rm.rm_hdr.rtm_tableid = crt->rt.rtable;
131 	rm.rm_hdr.rtm_priority = crt->host.priority;
132 
133 	if (strlen(crt->rt.label)) {
134 		rm.rm_hdr.rtm_addrs |= RTA_LABEL;
135 		sr.sr_len = sizeof(sr);
136 		if (snprintf(sr.sr_label, sizeof(sr.sr_label),
137 		    "%s", crt->rt.label) == -1)
138 			goto bad;
139 	}
140 
141 	if (crt->nr.ss.ss_family == AF_INET) {
142 		rm.rm_hdr.rtm_msglen = len =
143 		    sizeof(rm.rm_hdr) + sizeof(rm.rm_u.u4);
144 
145 		bcopy(&sr, &rm.rm_u.u4.rm_label, sizeof(sr));
146 
147 		s4 = &rm.rm_u.u4.rm_dst;
148 		s4->sin_family = AF_INET;
149 		s4->sin_len = sizeof(rm.rm_u.u4.rm_dst);
150 		s4->sin_addr.s_addr =
151 		    ((struct sockaddr_in *)&crt->nr.ss)->sin_addr.s_addr;
152 
153 		s4 = &rm.rm_u.u4.rm_gateway;
154 		s4->sin_family = AF_INET;
155 		s4->sin_len = sizeof(rm.rm_u.u4.rm_gateway);
156 		s4->sin_addr.s_addr =
157 		    ((struct sockaddr_in *)gw)->sin_addr.s_addr;
158 
159 		rm.rm_hdr.rtm_addrs |= RTA_NETMASK;
160 		s4 = &rm.rm_u.u4.rm_netmask;
161 		s4->sin_family = AF_INET;
162 		s4->sin_len = sizeof(rm.rm_u.u4.rm_netmask);
163 		if (crt->nr.prefixlen)
164 			s4->sin_addr.s_addr =
165 			    htonl(0xffffffff << (32 - crt->nr.prefixlen));
166 		else if (crt->nr.prefixlen < 0)
167 			rm.rm_hdr.rtm_flags |= RTF_HOST;
168 	} else if (crt->nr.ss.ss_family == AF_INET6) {
169 		rm.rm_hdr.rtm_msglen = len =
170 		    sizeof(rm.rm_hdr) + sizeof(rm.rm_u.u6);
171 
172 		bcopy(&sr, &rm.rm_u.u6.rm_label, sizeof(sr));
173 
174 		s6 = &rm.rm_u.u6.rm_dst;
175 		bcopy(((struct sockaddr_in6 *)&crt->nr.ss),
176 		    s6, sizeof(*s6));
177 		s6->sin6_family = AF_INET6;
178 		s6->sin6_len = sizeof(*s6);
179 
180 		s6 = &rm.rm_u.u6.rm_gateway;
181 		bcopy(((struct sockaddr_in6 *)gw), s6, sizeof(*s6));
182 		s6->sin6_family = AF_INET6;
183 		s6->sin6_len = sizeof(*s6);
184 
185 		rm.rm_hdr.rtm_addrs |= RTA_NETMASK;
186 		s6 = &rm.rm_u.u6.rm_netmask;
187 		s6->sin6_family = AF_INET6;
188 		s6->sin6_len = sizeof(*s6);
189 		if (crt->nr.prefixlen) {
190 			for (i = 0; i < crt->nr.prefixlen / 8; i++)
191 				s6->sin6_addr.s6_addr[i] = 0xff;
192 			i = crt->nr.prefixlen % 8;
193 			if (i)
194 				s6->sin6_addr.s6_addr[crt->nr.prefixlen
195 				    / 8] = 0xff00 >> i;
196 		} else if (crt->nr.prefixlen < 0)
197 			rm.rm_hdr.rtm_flags |= RTF_HOST;
198 	} else
199 		fatal("%s: invalid address family", __func__);
200 
201  retry:
202 	if (write(env->sc_rtsock, &rm, len) == -1) {
203 		switch (errno) {
204 		case EEXIST:
205 		case ESRCH:
206 			if (rm.rm_hdr.rtm_type == RTM_ADD) {
207 				rm.rm_hdr.rtm_type = RTM_CHANGE;
208 				goto retry;
209 			} else if (rm.rm_hdr.rtm_type == RTM_DELETE) {
210 				/* Ignore */
211 				break;
212 			}
213 			/* FALLTHROUGH */
214 		default:
215 			goto bad;
216 		}
217 	}
218 
219 	log_debug("%s: gateway %s %s", __func__, gwname,
220 	    HOST_ISUP(crt->up) ? "added" : "deleted");
221 
222 	return (0);
223 
224  bad:
225 	log_debug("%s: failed to %s gateway %s: %d %s", __func__,
226 	    HOST_ISUP(crt->up) ? "add" : "delete", gwname,
227 	    errno, strerror(errno));
228 
229 	return (-1);
230 }
231