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