1 /* $OpenBSD: roaming.c,v 1.5 2010/05/26 19:18:10 millert 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/tree.h> 23 #include <sys/ioctl.h> 24 25 #include <net/if.h> 26 #include <net/if_dl.h> 27 #include <net/if_media.h> 28 #include <net/if_arp.h> 29 #include <net/if_llc.h> 30 #include <net/route.h> 31 32 #include <netinet/in.h> 33 #include <netinet/if_ether.h> 34 #include <arpa/inet.h> 35 36 #include <stdlib.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <fcntl.h> 41 #include <errno.h> 42 43 #include "hostapd.h" 44 45 int hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int); 46 int hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int); 47 48 void 49 hostapd_roaming_init(struct hostapd_config *cfg) 50 { 51 struct hostapd_iapp *iapp = &cfg->c_iapp; 52 struct hostapd_apme *apme; 53 struct ifreq ifr; 54 int v; 55 56 if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 || 57 (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE) == 0) 58 return; 59 60 if ((cfg->c_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) == -1) 61 hostapd_fatal("failed to init inet socket: %s\n", 62 strerror(errno)); 63 64 v = 0; 65 if (setsockopt(cfg->c_rtsock, SOL_SOCKET, SO_USELOOPBACK, 66 &v, sizeof(v)) == -1) 67 hostapd_fatal("failed to setup inet socket: %s\n", 68 strerror(errno)); 69 70 TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) { 71 bzero(&ifr, sizeof(ifr)); 72 (void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name)); 73 if (ioctl(cfg->c_apme_ctl, SIOCGIFADDR, &ifr) == -1) 74 hostapd_fatal("ioctl %s on \"%s\" failed: %s\n", 75 "SIOCGIFADDR", ifr.ifr_name, strerror(errno)); 76 bcopy(&ifr.ifr_addr, &apme->a_addr, 77 sizeof(struct sockaddr_in)); 78 hostapd_log(HOSTAPD_LOG_VERBOSE, 79 "%s/%s: using gateway address %s", 80 apme->a_iface, iapp->i_iface, 81 inet_ntoa(apme->a_addr.sin_addr), apme->a_iface); 82 } 83 } 84 85 void 86 hostapd_roaming_term(struct hostapd_apme *apme) 87 { 88 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 89 struct hostapd_iapp *iapp = &cfg->c_iapp; 90 struct hostapd_entry *entry; 91 92 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE && 93 iapp->i_route_tbl != NULL) { 94 RB_FOREACH(entry, hostapd_tree, &iapp->i_route_tbl->t_tree) { 95 if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 96 continue; 97 (void)hostapd_roaming_rt(apme, &entry->e_inaddr, 0); 98 } 99 } 100 101 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS && 102 iapp->i_addr_tbl != NULL) { 103 RB_FOREACH(entry, hostapd_tree, &iapp->i_addr_tbl->t_tree) { 104 if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 105 continue; 106 (void)hostapd_roaming_addr(apme, &entry->e_inaddr, 0); 107 } 108 } 109 } 110 111 int 112 hostapd_roaming(struct hostapd_apme *apme, struct hostapd_node *node, int add) 113 { 114 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 115 struct hostapd_iapp *iapp = &cfg->c_iapp; 116 struct hostapd_entry *entry; 117 int ret; 118 119 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS && 120 iapp->i_addr_tbl != NULL) { 121 if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl, 122 node->ni_macaddr)) == NULL || 123 (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 124 return (ESRCH); 125 if ((ret = hostapd_roaming_addr(apme, &entry->e_inaddr, 126 add)) != 0) 127 return (ret); 128 } 129 130 if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE && 131 iapp->i_route_tbl != NULL) { 132 if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl, 133 node->ni_macaddr)) == NULL || 134 (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) 135 return (ESRCH); 136 if ((ret = hostapd_roaming_rt(apme, &entry->e_inaddr, 137 add)) != 0) 138 return (ret); 139 } 140 141 return (0); 142 } 143 144 145 int 146 hostapd_roaming_addr(struct hostapd_apme *apme, struct hostapd_inaddr *addr, 147 int add) 148 { 149 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 150 struct hostapd_iapp *iapp = &cfg->c_iapp; 151 struct sockaddr_in *ifaddr, *ifmask, *ifbroadaddr; 152 struct ifaliasreq ifra; 153 154 bzero(&ifra, sizeof(ifra)); 155 156 ifaddr = (struct sockaddr_in *)&ifra.ifra_addr; 157 ifaddr->sin_family = AF_INET; 158 ifaddr->sin_len = sizeof(struct sockaddr_in); 159 ifaddr->sin_addr.s_addr = addr->in_v4.s_addr; 160 161 ifbroadaddr = (struct sockaddr_in *)&ifra.ifra_broadaddr; 162 ifbroadaddr->sin_family = AF_INET; 163 ifbroadaddr->sin_len = sizeof(struct sockaddr_in); 164 ifbroadaddr->sin_addr.s_addr = 165 addr->in_v4.s_addr | htonl(0xffffffffUL >> addr->in_netmask); 166 167 if (add) { 168 ifmask = (struct sockaddr_in *)&ifra.ifra_mask; 169 ifmask->sin_family = AF_INET; 170 ifmask->sin_len = sizeof(struct sockaddr_in); 171 ifmask->sin_addr.s_addr = 172 htonl(0xffffffff << (32 - addr->in_netmask)); 173 } 174 175 (void)strlcpy(ifra.ifra_name, apme->a_iface, sizeof(ifra.ifra_name)); 176 if (ioctl(cfg->c_apme_ctl, SIOCDIFADDR, &ifra) < 0) { 177 if (errno != EADDRNOTAVAIL) { 178 hostapd_log(HOSTAPD_LOG_VERBOSE, 179 "%s/%s: failed to delete address %s", 180 apme->a_iface, iapp->i_iface, 181 inet_ntoa(addr->in_v4)); 182 return (errno); 183 } 184 } 185 if (add && ioctl(cfg->c_apme_ctl, SIOCAIFADDR, &ifra) < 0) { 186 if (errno != EEXIST) { 187 hostapd_log(HOSTAPD_LOG_VERBOSE, 188 "%s/%s: failed to add address %s", 189 apme->a_iface, iapp->i_iface, 190 inet_ntoa(addr->in_v4)); 191 return (errno); 192 } 193 } 194 195 hostapd_log(HOSTAPD_LOG_VERBOSE, 196 "%s/%s: %s address %s", 197 apme->a_iface, iapp->i_iface, 198 add ? "added" : "deleted", 199 inet_ntoa(addr->in_v4)); 200 201 return (0); 202 } 203 204 int 205 hostapd_roaming_rt(struct hostapd_apme *apme, struct hostapd_inaddr *addr, 206 int add) 207 { 208 struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; 209 struct hostapd_iapp *iapp = &cfg->c_iapp; 210 struct { 211 struct rt_msghdr rm_hdr; 212 struct sockaddr_in rm_dst; 213 struct sockaddr_in rm_gateway; 214 struct sockaddr_in rm_netmask; 215 struct sockaddr_rtlabel rm_label; 216 } rm; 217 size_t len = sizeof(rm); 218 219 bzero(&rm, len); 220 221 rm.rm_hdr.rtm_msglen = len; 222 rm.rm_hdr.rtm_version = RTM_VERSION; 223 rm.rm_hdr.rtm_type = add ? RTM_CHANGE : RTM_DELETE; 224 rm.rm_hdr.rtm_flags = RTF_STATIC; 225 rm.rm_hdr.rtm_seq = cfg->c_rtseq++; 226 rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_LABEL; 227 rm.rm_hdr.rtm_hdrlen = sizeof(struct rt_msghdr); 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