1 /* $OpenBSD: roaming.c,v 1.8 2019/06/28 13:32:47 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006 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/socket.h> 21 #include <sys/tree.h> 22 #include <sys/ioctl.h> 23 24 #include <net/if.h> 25 #include <net/if_media.h> 26 #include <net/if_arp.h> 27 #include <net/if_llc.h> 28 #include <net/route.h> 29 30 #include <netinet/in.h> 31 #include <netinet/if_ether.h> 32 #include <arpa/inet.h> 33 34 #include <stdlib.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <fcntl.h> 39 #include <errno.h> 40 #include <limits.h> 41 42 #include "hostapd.h" 43 44 int hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int); 45 int hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int); 46 47 void 48 hostapd_roaming_init(struct hostapd_config *cfg) 49 { 50 struct hostapd_iapp *iapp = &cfg->c_iapp; 51 struct hostapd_apme *apme; 52 struct ifreq ifr; 53 int v; 54 55 if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 || 56 (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE) == 0) 57 return; 58 59 if ((cfg->c_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) == -1) 60 hostapd_fatal("failed to init inet socket: %s\n", 61 strerror(errno)); 62 63 v = 0; 64 if (setsockopt(cfg->c_rtsock, SOL_SOCKET, SO_USELOOPBACK, 65 &v, sizeof(v)) == -1) 66 hostapd_fatal("failed to setup inet socket: %s\n", 67 strerror(errno)); 68 69 TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) { 70 bzero(&ifr, sizeof(ifr)); 71 (void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name)); 72 if (ioctl(cfg->c_apme_ctl, SIOCGIFADDR, &ifr) == -1) 73 hostapd_fatal("ioctl %s on \"%s\" failed: %s\n", 74 "SIOCGIFADDR", ifr.ifr_name, strerror(errno)); 75 bcopy(&ifr.ifr_addr, &apme->a_addr, 76 sizeof(struct sockaddr_in)); 77 hostapd_log(HOSTAPD_LOG_VERBOSE, 78 "%s/%s: using gateway address %s", 79 apme->a_iface, iapp->i_iface, 80 inet_ntoa(apme->a_addr.sin_addr), apme->a_iface); 81 } 82 } 83 84 void 85 hostapd_roaming_term(struct hostapd_apme *apme) 86 { 87 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 88 struct hostapd_iapp *iapp = &cfg->c_iapp; 89 struct hostapd_entry *entry; 90 91 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE && 92 iapp->i_route_tbl != NULL) { 93 RB_FOREACH(entry, hostapd_tree, &iapp->i_route_tbl->t_tree) { 94 if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 95 continue; 96 (void)hostapd_roaming_rt(apme, &entry->e_inaddr, 0); 97 } 98 } 99 100 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS && 101 iapp->i_addr_tbl != NULL) { 102 RB_FOREACH(entry, hostapd_tree, &iapp->i_addr_tbl->t_tree) { 103 if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 104 continue; 105 (void)hostapd_roaming_addr(apme, &entry->e_inaddr, 0); 106 } 107 } 108 } 109 110 int 111 hostapd_roaming(struct hostapd_apme *apme, struct hostapd_node *node, int add) 112 { 113 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 114 struct hostapd_iapp *iapp = &cfg->c_iapp; 115 struct hostapd_entry *entry; 116 int ret; 117 118 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS && 119 iapp->i_addr_tbl != NULL) { 120 if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl, 121 node->ni_macaddr)) == NULL || 122 (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 123 return (ESRCH); 124 if ((ret = hostapd_roaming_addr(apme, &entry->e_inaddr, 125 add)) != 0) 126 return (ret); 127 } 128 129 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE && 130 iapp->i_route_tbl != NULL) { 131 if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl, 132 node->ni_macaddr)) == NULL || 133 (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 134 return (ESRCH); 135 if ((ret = hostapd_roaming_rt(apme, &entry->e_inaddr, 136 add)) != 0) 137 return (ret); 138 } 139 140 return (0); 141 } 142 143 144 int 145 hostapd_roaming_addr(struct hostapd_apme *apme, struct hostapd_inaddr *addr, 146 int add) 147 { 148 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 149 struct hostapd_iapp *iapp = &cfg->c_iapp; 150 struct sockaddr_in *ifaddr, *ifmask, *ifbroadaddr; 151 struct ifaliasreq ifra; 152 153 bzero(&ifra, sizeof(ifra)); 154 155 ifaddr = (struct sockaddr_in *)&ifra.ifra_addr; 156 ifaddr->sin_family = AF_INET; 157 ifaddr->sin_len = sizeof(struct sockaddr_in); 158 ifaddr->sin_addr.s_addr = addr->in_v4.s_addr; 159 160 ifbroadaddr = (struct sockaddr_in *)&ifra.ifra_broadaddr; 161 ifbroadaddr->sin_family = AF_INET; 162 ifbroadaddr->sin_len = sizeof(struct sockaddr_in); 163 ifbroadaddr->sin_addr.s_addr = 164 addr->in_v4.s_addr | htonl(0xffffffffUL >> addr->in_netmask); 165 166 if (add) { 167 ifmask = (struct sockaddr_in *)&ifra.ifra_mask; 168 ifmask->sin_family = AF_INET; 169 ifmask->sin_len = sizeof(struct sockaddr_in); 170 ifmask->sin_addr.s_addr = 171 htonl(0xffffffff << (32 - addr->in_netmask)); 172 } 173 174 (void)strlcpy(ifra.ifra_name, apme->a_iface, sizeof(ifra.ifra_name)); 175 if (ioctl(cfg->c_apme_ctl, SIOCDIFADDR, &ifra) == -1) { 176 if (errno != EADDRNOTAVAIL) { 177 hostapd_log(HOSTAPD_LOG_VERBOSE, 178 "%s/%s: failed to delete address %s", 179 apme->a_iface, iapp->i_iface, 180 inet_ntoa(addr->in_v4)); 181 return (errno); 182 } 183 } 184 if (add && ioctl(cfg->c_apme_ctl, SIOCAIFADDR, &ifra) == -1) { 185 if (errno != EEXIST) { 186 hostapd_log(HOSTAPD_LOG_VERBOSE, 187 "%s/%s: failed to add address %s", 188 apme->a_iface, iapp->i_iface, 189 inet_ntoa(addr->in_v4)); 190 return (errno); 191 } 192 } 193 194 hostapd_log(HOSTAPD_LOG_VERBOSE, 195 "%s/%s: %s address %s", 196 apme->a_iface, iapp->i_iface, 197 add ? "added" : "deleted", 198 inet_ntoa(addr->in_v4)); 199 200 return (0); 201 } 202 203 int 204 hostapd_roaming_rt(struct hostapd_apme *apme, struct hostapd_inaddr *addr, 205 int add) 206 { 207 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 208 struct hostapd_iapp *iapp = &cfg->c_iapp; 209 struct { 210 struct rt_msghdr rm_hdr; 211 struct sockaddr_in rm_dst; 212 struct sockaddr_in rm_gateway; 213 struct sockaddr_in rm_netmask; 214 struct sockaddr_rtlabel rm_label; 215 } rm; 216 size_t len = sizeof(rm); 217 218 bzero(&rm, len); 219 220 rm.rm_hdr.rtm_msglen = len; 221 rm.rm_hdr.rtm_version = RTM_VERSION; 222 rm.rm_hdr.rtm_type = add ? RTM_CHANGE : RTM_DELETE; 223 rm.rm_hdr.rtm_flags = RTF_STATIC; 224 rm.rm_hdr.rtm_seq = cfg->c_rtseq++; 225 rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_LABEL; 226 rm.rm_hdr.rtm_hdrlen = sizeof(struct rt_msghdr); 227 228 rm.rm_dst.sin_family = AF_INET; 229 rm.rm_dst.sin_len = sizeof(rm.rm_dst); 230 rm.rm_dst.sin_addr.s_addr = addr->in_v4.s_addr; 231 232 rm.rm_gateway.sin_family = AF_INET; 233 rm.rm_gateway.sin_len = sizeof(rm.rm_gateway); 234 rm.rm_gateway.sin_addr.s_addr = apme->a_addr.sin_addr.s_addr; 235 236 rm.rm_hdr.rtm_addrs |= RTA_NETMASK; 237 rm.rm_netmask.sin_len = sizeof(rm.rm_netmask); 238 rm.rm_netmask.sin_family = AF_INET; 239 if (addr->in_netmask) 240 rm.rm_netmask.sin_addr.s_addr = 241 htonl(0xffffffff << (32 - addr->in_netmask)); 242 else if (addr->in_netmask < 0) 243 rm.rm_hdr.rtm_flags |= RTF_HOST; 244 245 rm.rm_label.sr_len = sizeof(rm.rm_label); 246 if (snprintf(rm.rm_label.sr_label, sizeof(rm.rm_label.sr_label), 247 "apme-%s", apme->a_iface) == -1) 248 goto bad; 249 250 retry: 251 if (write(cfg->c_rtsock, &rm, len) == -1) { 252 switch (errno) { 253 case ESRCH: 254 if (rm.rm_hdr.rtm_type == RTM_CHANGE) { 255 rm.rm_hdr.rtm_type = RTM_ADD; 256 goto retry; 257 } else if (rm.rm_hdr.rtm_type == RTM_DELETE) { 258 /* Ignore */ 259 break; 260 } 261 /* FALLTHROUGH */ 262 default: 263 goto bad; 264 } 265 } 266 267 hostapd_log(HOSTAPD_LOG_VERBOSE, 268 "%s/%s: %s route to %s", 269 apme->a_iface, iapp->i_iface, 270 add ? "added" : "deleted", 271 inet_ntoa(addr->in_v4)); 272 273 return (0); 274 275 bad: 276 hostapd_log(HOSTAPD_LOG_VERBOSE, 277 "%s/%s: failed to %s route to %s: %s", 278 apme->a_iface, iapp->i_iface, 279 add ? "add" : "delete", 280 inet_ntoa(addr->in_v4), 281 strerror(errno)); 282 283 return (ESRCH); 284 } 285 286 int 287 hostapd_roaming_add(struct hostapd_apme *apme, struct hostapd_node *node) 288 { 289 return (hostapd_priv_roaming(apme, node, 1)); 290 } 291 292 int 293 hostapd_roaming_del(struct hostapd_apme *apme, struct hostapd_node *node) 294 { 295 return (hostapd_priv_roaming(apme, node, 0)); 296 } 297 298