1 /*- 2 * Copyright (c) 2015 Dmitry Chagin <dchagin@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/kernel.h> 29 #include <sys/ctype.h> 30 #include <sys/eventhandler.h> 31 #include <sys/jail.h> 32 #include <sys/socket.h> 33 #include <sys/sysctl.h> 34 #include <net/if.h> 35 #include <net/if_dl.h> 36 #include <net/if_types.h> 37 #include <net/if_var.h> 38 #include <net/if_private.h> 39 #include <net/vnet.h> 40 41 #include <compat/linux/linux.h> 42 #include <compat/linux/linux_common.h> 43 #include <compat/linux/linux_mib.h> 44 45 _Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ"); 46 47 static bool use_real_ifnames = false; 48 SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN, 49 &use_real_ifnames, 0, 50 "Use FreeBSD interface names instead of generating ethN aliases"); 51 52 VNET_DEFINE_STATIC(struct unrhdr *, linux_eth_unr); 53 #define V_linux_eth_unr VNET(linux_eth_unr) 54 55 static eventhandler_tag ifnet_arrival_tag; 56 static eventhandler_tag ifnet_departure_tag; 57 58 static void 59 linux_ifnet_arrival(void *arg __unused, struct ifnet *ifp) 60 { 61 if (ifp->if_type == IFT_ETHER) 62 ifp->if_linux_ethno = alloc_unr(V_linux_eth_unr); 63 } 64 65 static void 66 linux_ifnet_departure(void *arg __unused, struct ifnet *ifp) 67 { 68 if (ifp->if_type == IFT_ETHER) 69 free_unr(V_linux_eth_unr, ifp->if_linux_ethno); 70 } 71 72 void 73 linux_ifnet_init(void) 74 { 75 ifnet_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event, 76 linux_ifnet_arrival, NULL, EVENTHANDLER_PRI_FIRST); 77 ifnet_departure_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 78 linux_ifnet_departure, NULL, EVENTHANDLER_PRI_LAST); 79 } 80 81 void 82 linux_ifnet_uninit(void) 83 { 84 EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ifnet_arrival_tag); 85 EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifnet_departure_tag); 86 } 87 88 static void 89 linux_ifnet_vnet_init(void *arg __unused) 90 { 91 struct epoch_tracker et; 92 struct if_iter it; 93 if_t ifp; 94 95 V_linux_eth_unr = new_unrhdr(0, INT_MAX, NULL); 96 NET_EPOCH_ENTER(et); 97 for (ifp = if_iter_start(&it); ifp != NULL; ifp = if_iter_next(&it)) 98 linux_ifnet_arrival(NULL, ifp); 99 NET_EPOCH_EXIT(et); 100 } 101 VNET_SYSINIT(linux_ifnet_vnet_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, 102 linux_ifnet_vnet_init, NULL); 103 104 static void 105 linux_ifnet_vnet_uninit(void *arg __unused) 106 { 107 /* 108 * At a normal vnet shutdown all interfaces are gone at this point. 109 * But when we kldunload linux.ko, the vnet_deregister_sysuninit() 110 * would call this function for the default vnet. 111 */ 112 if (IS_DEFAULT_VNET(curvnet)) 113 clear_unrhdr(V_linux_eth_unr); 114 delete_unrhdr(V_linux_eth_unr); 115 } 116 VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, 117 linux_ifnet_vnet_uninit, NULL); 118 119 /* 120 * Translate a FreeBSD interface name to a Linux interface name 121 * by interface index, and return the number of bytes copied to lxname. 122 */ 123 int 124 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len) 125 { 126 struct epoch_tracker et; 127 struct ifnet *ifp; 128 int ret; 129 130 ret = 0; 131 CURVNET_SET(TD_TO_VNET(curthread)); 132 NET_EPOCH_ENTER(et); 133 ifp = ifnet_byindex(idx); 134 if (ifp != NULL) 135 ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); 136 NET_EPOCH_EXIT(et); 137 CURVNET_RESTORE(); 138 return (ret); 139 } 140 141 /* 142 * Translate a FreeBSD interface name to a Linux interface name, 143 * and return the number of bytes copied to lxname, 0 if interface 144 * not found, -1 on error. 145 */ 146 int 147 ifname_bsd_to_linux_ifp(const struct ifnet *ifp, char *lxname, size_t len) 148 { 149 /* 150 * Linux loopback interface name is lo (not lo0), 151 * we translate lo to lo0, loX to loX. 152 */ 153 if (ifp->if_type == IFT_LOOP && 154 strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0) 155 return (strlcpy(lxname, "lo", len)); 156 157 /* Short-circuit non ethernet interfaces. */ 158 if (ifp->if_type != IFT_ETHER || use_real_ifnames) 159 return (strlcpy(lxname, ifp->if_xname, len)); 160 161 /* Determine the (relative) unit number for ethernet interfaces. */ 162 return (snprintf(lxname, len, "eth%d", ifp->if_linux_ethno)); 163 } 164 165 /* 166 * Translate a Linux interface name to a FreeBSD interface name, 167 * and return the associated ifnet structure 168 * bsdname and lxname need to be least IFNAMSIZ bytes long, but 169 * can point to the same buffer. 170 */ 171 struct ifname_linux_to_ifp_cb_s { 172 bool is_lo; 173 bool is_eth; 174 int unit; 175 const char *lxname; 176 if_t ifp; 177 }; 178 179 static int 180 ifname_linux_to_ifp_cb(if_t ifp, void *arg) 181 { 182 struct ifname_linux_to_ifp_cb_s *cbs = arg; 183 184 NET_EPOCH_ASSERT(); 185 186 /* 187 * Allow Linux programs to use FreeBSD names. Don't presume 188 * we never have an interface named "eth", so don't make 189 * the test optional based on is_eth. 190 */ 191 if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) 192 goto out; 193 if (cbs->is_eth && ifp->if_type == IFT_ETHER && 194 ifp->if_linux_ethno == cbs->unit) 195 goto out; 196 if (cbs->is_lo && ifp->if_type == IFT_LOOP) 197 goto out; 198 return (0); 199 200 out: 201 cbs->ifp = ifp; 202 return (1); 203 } 204 205 struct ifnet * 206 ifname_linux_to_ifp(const char *lxname) 207 { 208 struct ifname_linux_to_ifp_cb_s arg = { 209 .lxname = lxname, 210 }; 211 int len; 212 char *ep; 213 214 NET_EPOCH_ASSERT(); 215 216 for (len = 0; len < LINUX_IFNAMSIZ; ++len) 217 if (!isalpha(lxname[len]) || lxname[len] == '\0') 218 break; 219 if (len == 0 || len == LINUX_IFNAMSIZ) 220 return (NULL); 221 /* 222 * Linux loopback interface name is lo (not lo0), 223 * we translate lo to lo0, loX to loX. 224 */ 225 arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0); 226 arg.unit = (int)strtoul(lxname + len, &ep, 10); 227 if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && 228 arg.is_lo == 0) 229 return (NULL); 230 arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); 231 232 if_foreach(ifname_linux_to_ifp_cb, &arg); 233 return (arg.ifp); 234 } 235 236 int 237 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) 238 { 239 struct epoch_tracker et; 240 struct ifnet *ifp; 241 242 CURVNET_SET(TD_TO_VNET(td)); 243 NET_EPOCH_ENTER(et); 244 ifp = ifname_linux_to_ifp(lxname); 245 if (ifp != NULL && bsdname != NULL) 246 strlcpy(bsdname, if_name(ifp), IFNAMSIZ); 247 NET_EPOCH_EXIT(et); 248 CURVNET_RESTORE(); 249 return (ifp != NULL ? 0 : EINVAL); 250 } 251 252 unsigned short 253 linux_ifflags(struct ifnet *ifp) 254 { 255 unsigned short flags; 256 257 NET_EPOCH_ASSERT(); 258 259 flags = if_getflags(ifp) | if_getdrvflags(ifp); 260 return (bsd_to_linux_ifflags(flags)); 261 } 262 263 unsigned short 264 bsd_to_linux_ifflags(int fl) 265 { 266 unsigned short flags = 0; 267 268 if (fl & IFF_UP) 269 flags |= LINUX_IFF_UP; 270 if (fl & IFF_BROADCAST) 271 flags |= LINUX_IFF_BROADCAST; 272 if (fl & IFF_DEBUG) 273 flags |= LINUX_IFF_DEBUG; 274 if (fl & IFF_LOOPBACK) 275 flags |= LINUX_IFF_LOOPBACK; 276 if (fl & IFF_POINTOPOINT) 277 flags |= LINUX_IFF_POINTOPOINT; 278 if (fl & IFF_DRV_RUNNING) 279 flags |= LINUX_IFF_RUNNING; 280 if (fl & IFF_NOARP) 281 flags |= LINUX_IFF_NOARP; 282 if (fl & IFF_PROMISC) 283 flags |= LINUX_IFF_PROMISC; 284 if (fl & IFF_ALLMULTI) 285 flags |= LINUX_IFF_ALLMULTI; 286 if (fl & IFF_MULTICAST) 287 flags |= LINUX_IFF_MULTICAST; 288 return (flags); 289 } 290 291 static u_int 292 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count) 293 { 294 struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; 295 struct l_sockaddr *lsa = arg; 296 297 if (count > 0) 298 return (0); 299 if (sdl->sdl_type != IFT_ETHER) 300 return (0); 301 bzero(lsa, sizeof(*lsa)); 302 lsa->sa_family = LINUX_ARPHRD_ETHER; 303 bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); 304 return (1); 305 } 306 307 int 308 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa) 309 { 310 311 NET_EPOCH_ASSERT(); 312 313 if (ifp->if_type == IFT_LOOP) { 314 bzero(lsa, sizeof(*lsa)); 315 lsa->sa_family = LINUX_ARPHRD_LOOPBACK; 316 return (0); 317 } 318 if (ifp->if_type != IFT_ETHER) 319 return (ENOENT); 320 if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) 321 return (0); 322 return (ENOENT); 323 } 324