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 37void 38init_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 54void 55sync_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 90static void 91pfe_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 122int 123pfe_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