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