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