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