Blob


1 /* $OpenBSD: pfe_route.c,v 1.15 2026/03/02 19:28:01 rsadowski Exp $ */
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 */
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
24 #include <netinet/in.h>
25 #include <net/route.h>
26 #include <arpa/inet.h>
28 #include <limits.h>
29 #include <stddef.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
35 #include "relayd.h"
36 #include "log.h"
38 void
39 init_routes(struct relayd *env)
40 {
41 u_int rtfilter;
43 if (!(env->sc_conf.flags & F_NEEDRT))
44 return;
46 if ((env->sc_rtsock = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
47 fatal("%s: failed to open routing socket", __func__);
49 rtfilter = ROUTE_FILTER(0);
50 if (setsockopt(env->sc_rtsock, AF_ROUTE, ROUTE_MSGFILTER,
51 &rtfilter, sizeof(rtfilter)) == -1)
52 fatal("%s: ROUTE_MSGFILTER", __func__);
53 }
55 void
56 sync_routes(struct relayd *env, struct router *rt)
57 {
58 struct netroute *nr;
59 struct host *host;
60 char buf[HOST_NAME_MAX+1];
61 struct ctl_netroute crt;
63 if (!(env->sc_conf.flags & F_NEEDRT))
64 return;
66 TAILQ_FOREACH(nr, &rt->rt_netroutes, nr_entry) {
67 print_host(&nr->nr_conf.ss, buf, sizeof(buf));
68 TAILQ_FOREACH(host, &rt->rt_gwtable->hosts, entry) {
69 if (host->up == HOST_UNKNOWN)
70 continue;
72 log_debug("%s: "
73 "router %s route %s/%d gateway %s %s priority %d",
74 __func__,
75 rt->rt_conf.name, buf, nr->nr_conf.prefixlen,
76 host->conf.name,
77 HOST_ISUP(host->up) ? "up" : "down",
78 host->conf.priority);
80 crt.up = host->up;
81 memcpy(&crt.nr, &nr->nr_conf, sizeof(nr->nr_conf));
82 memcpy(&crt.host, &host->conf, sizeof(host->conf));
83 memcpy(&crt.rt, &rt->rt_conf, sizeof(rt->rt_conf));
85 proc_compose(env->sc_ps, PROC_PARENT,
86 IMSG_RTMSG, &crt, sizeof(crt));
87 }
88 }
89 }
91 static void
92 pfe_apply_prefixlen(struct sockaddr_storage *ss, int af, int len)
93 {
94 int q, r, off;
95 uint8_t *b = (uint8_t *)ss;
97 q = len >> 3;
98 r = len & 7;
100 bzero(ss, sizeof(*ss));
101 ss->ss_family = af;
102 switch (af) {
103 case AF_INET:
104 ss->ss_len = sizeof(struct sockaddr_in);
105 off = offsetof(struct sockaddr_in, sin_addr);
106 break;
107 case AF_INET6:
108 ss->ss_len = sizeof(struct sockaddr_in6);
109 off = offsetof(struct sockaddr_in6, sin6_addr);
110 break;
111 default:
112 fatal("%s: invalid address family", __func__);
114 if (q > 0)
115 memset(b + off, 0xff, q);
116 if (r > 0)
117 b[off + q] = (0xff00 >> r) & 0xff;
120 #define ROUNDUP(a) \
121 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
123 int
124 pfe_route(struct relayd *env, struct ctl_netroute *crt)
126 struct iovec iov[5];
127 struct rt_msghdr hdr;
128 struct sockaddr_storage dst, gw, mask, label;
129 struct sockaddr_rtlabel *sr = (struct sockaddr_rtlabel *)&label;
130 int iovcnt = 0;
131 char *gwname;
133 bzero(&hdr, sizeof(hdr));
134 hdr.rtm_msglen = sizeof(hdr);
135 hdr.rtm_version = RTM_VERSION;
136 hdr.rtm_type = HOST_ISUP(crt->up) ? RTM_ADD : RTM_DELETE;
137 hdr.rtm_flags = RTF_STATIC | RTF_GATEWAY | RTF_MPATH;
138 hdr.rtm_seq = env->sc_rtseq++;
139 hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
140 hdr.rtm_tableid = crt->rt.rtable;
141 hdr.rtm_priority = crt->host.priority;
143 iov[iovcnt].iov_base = &hdr;
144 iov[iovcnt++].iov_len = sizeof(hdr);
146 dst = crt->nr.ss;
147 gw = crt->host.ss;
148 gwname = crt->host.name;
149 pfe_apply_prefixlen(&mask, dst.ss_family, crt->nr.prefixlen);
151 iov[iovcnt].iov_base = &dst;
152 iov[iovcnt++].iov_len = ROUNDUP(dst.ss_len);
153 hdr.rtm_msglen += ROUNDUP(dst.ss_len);
155 iov[iovcnt].iov_base = &gw;
156 iov[iovcnt++].iov_len = ROUNDUP(gw.ss_len);
157 hdr.rtm_msglen += ROUNDUP(gw.ss_len);
159 iov[iovcnt].iov_base = &mask;
160 iov[iovcnt++].iov_len = ROUNDUP(mask.ss_len);
161 hdr.rtm_msglen += ROUNDUP(mask.ss_len);
163 if (strlen(crt->rt.label)) {
164 sr->sr_len = sizeof(*sr);
165 strlcpy(sr->sr_label, crt->rt.label, sizeof(sr->sr_label));
167 iov[iovcnt].iov_base = &label;
168 iov[iovcnt++].iov_len = ROUNDUP(label.ss_len);
169 hdr.rtm_msglen += ROUNDUP(label.ss_len);
170 hdr.rtm_addrs |= RTA_LABEL;
173 retry:
174 if (writev(env->sc_rtsock, iov, iovcnt) == -1) {
175 switch (errno) {
176 case EEXIST:
177 case ESRCH:
178 if (hdr.rtm_type == RTM_ADD) {
179 hdr.rtm_type = RTM_CHANGE;
180 goto retry;
181 } else if (hdr.rtm_type == RTM_DELETE) {
182 /* Ignore */
183 break;
185 /* FALLTHROUGH */
186 default:
187 goto bad;
191 log_debug("%s: gateway %s %s", __func__, gwname,
192 HOST_ISUP(crt->up) ? "added" : "deleted");
194 return (0);
196 bad:
197 log_debug("%s: failed to %s gateway %s: %d %s", __func__,
198 HOST_ISUP(crt->up) ? "add" : "delete", gwname,
199 errno, strerror(errno));
201 return (-1);