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/ctype.h> 29 #include <sys/eventhandler.h> 30 #include <sys/jail.h> 31 #include <sys/socket.h> 32 #include <sys/sysctl.h> 33 #include <net/if.h> 34 #include <net/if_dl.h> 35 #include <net/if_types.h> 36 #include <net/if_var.h> 37 #include <net/if_private.h> 38 #include <net/vnet.h> 39 40 #include <compat/linux/linux.h> 41 #include <compat/linux/linux_common.h> 42 #include <compat/linux/linux_mib.h> 43 44 _Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ"); 45 46 static bool use_real_ifnames = false; 47 SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN, 48 &use_real_ifnames, 0, 49 "Use FreeBSD interface names instead of generating ethN aliases"); 50 51 VNET_DEFINE_STATIC(struct unrhdr *, linux_eth_unr); 52 #define V_linux_eth_unr VNET(linux_eth_unr) 53 54 static eventhandler_tag ifnet_arrival_tag; 55 static eventhandler_tag ifnet_departure_tag; 56 57 static void 58 linux_ifnet_arrival(void *arg __unused, struct ifnet *ifp) 59 { 60 if (ifp->if_type == IFT_ETHER) 61 ifp->if_linux_ethno = alloc_unr(V_linux_eth_unr); 62 } 63 64 static void 65 linux_ifnet_departure(void *arg __unused, struct ifnet *ifp) 66 { 67 if (ifp->if_type == IFT_ETHER) 68 free_unr(V_linux_eth_unr, ifp->if_linux_ethno); 69 } 70 71 void 72 linux_ifnet_init(void) 73 { 74 ifnet_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event, 75 linux_ifnet_arrival, NULL, EVENTHANDLER_PRI_FIRST); 76 ifnet_departure_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 77 linux_ifnet_departure, NULL, EVENTHANDLER_PRI_LAST); 78 } 79 80 void 81 linux_ifnet_uninit(void) 82 { 83 EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ifnet_arrival_tag); 84 EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifnet_departure_tag); 85 } 86 87 static void 88 linux_ifnet_vnet_init(void *arg __unused) 89 { 90 struct epoch_tracker et; 91 struct if_iter it; 92 if_t ifp; 93 94 V_linux_eth_unr = new_unrhdr(0, INT_MAX, NULL); 95 NET_EPOCH_ENTER(et); 96 for (ifp = if_iter_start(&it); ifp != NULL; ifp = if_iter_next(&it)) 97 linux_ifnet_arrival(NULL, ifp); 98 NET_EPOCH_EXIT(et); 99 } 100 VNET_SYSINIT(linux_ifnet_vnet_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, 101 linux_ifnet_vnet_init, NULL); 102 103 #ifdef VIMAGE 104 static void 105 linux_ifnet_vnet_uninit(void *arg __unused) 106 { 107 delete_unrhdr(V_linux_eth_unr); 108 } 109 VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, 110 linux_ifnet_vnet_uninit, NULL); 111 #endif 112 113 /* 114 * Translate a FreeBSD interface name to a Linux interface name 115 * by interface index, and return the number of bytes copied to lxname. 116 */ 117 int 118 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len) 119 { 120 struct epoch_tracker et; 121 struct ifnet *ifp; 122 int ret; 123 124 ret = 0; 125 CURVNET_SET(TD_TO_VNET(curthread)); 126 NET_EPOCH_ENTER(et); 127 ifp = ifnet_byindex(idx); 128 if (ifp != NULL) 129 ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); 130 NET_EPOCH_EXIT(et); 131 CURVNET_RESTORE(); 132 return (ret); 133 } 134 135 /* 136 * Translate a FreeBSD interface name to a Linux interface name, 137 * and return the number of bytes copied to lxname, 0 if interface 138 * not found, -1 on error. 139 */ 140 int 141 ifname_bsd_to_linux_ifp(const struct ifnet *ifp, char *lxname, size_t len) 142 { 143 /* 144 * Linux loopback interface name is lo (not lo0), 145 * we translate lo to lo0, loX to loX. 146 */ 147 if (ifp->if_type == IFT_LOOP && 148 strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0) 149 return (strlcpy(lxname, "lo", len)); 150 151 /* Short-circuit non ethernet interfaces. */ 152 if (ifp->if_type != IFT_ETHER || use_real_ifnames) 153 return (strlcpy(lxname, ifp->if_xname, len)); 154 155 /* Determine the (relative) unit number for ethernet interfaces. */ 156 return (snprintf(lxname, len, "eth%d", ifp->if_linux_ethno)); 157 } 158 159 /* 160 * Translate a Linux interface name to a FreeBSD interface name, 161 * and return the associated ifnet structure 162 * bsdname and lxname need to be least IFNAMSIZ bytes long, but 163 * can point to the same buffer. 164 */ 165 struct ifname_linux_to_ifp_cb_s { 166 bool is_lo; 167 bool is_eth; 168 int unit; 169 const char *lxname; 170 if_t ifp; 171 }; 172 173 static int 174 ifname_linux_to_ifp_cb(if_t ifp, void *arg) 175 { 176 struct ifname_linux_to_ifp_cb_s *cbs = arg; 177 178 NET_EPOCH_ASSERT(); 179 180 /* 181 * Allow Linux programs to use FreeBSD names. Don't presume 182 * we never have an interface named "eth", so don't make 183 * the test optional based on is_eth. 184 */ 185 if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) 186 goto out; 187 if (cbs->is_eth && ifp->if_type == IFT_ETHER && 188 ifp->if_linux_ethno == cbs->unit) 189 goto out; 190 if (cbs->is_lo && ifp->if_type == IFT_LOOP) 191 goto out; 192 return (0); 193 194 out: 195 cbs->ifp = ifp; 196 return (1); 197 } 198 199 struct ifnet * 200 ifname_linux_to_ifp(const char *lxname) 201 { 202 struct ifname_linux_to_ifp_cb_s arg = { 203 .lxname = lxname, 204 }; 205 int len; 206 char *ep; 207 208 NET_EPOCH_ASSERT(); 209 210 for (len = 0; len < LINUX_IFNAMSIZ; ++len) 211 if (!isalpha(lxname[len]) || lxname[len] == '\0') 212 break; 213 if (len == 0 || len == LINUX_IFNAMSIZ) 214 return (NULL); 215 /* 216 * Linux loopback interface name is lo (not lo0), 217 * we translate lo to lo0, loX to loX. 218 */ 219 arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0); 220 arg.unit = (int)strtoul(lxname + len, &ep, 10); 221 if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && 222 arg.is_lo == 0) 223 return (NULL); 224 arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); 225 226 if_foreach(ifname_linux_to_ifp_cb, &arg); 227 return (arg.ifp); 228 } 229 230 int 231 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) 232 { 233 struct epoch_tracker et; 234 struct ifnet *ifp; 235 236 CURVNET_SET(TD_TO_VNET(td)); 237 NET_EPOCH_ENTER(et); 238 ifp = ifname_linux_to_ifp(lxname); 239 if (ifp != NULL && bsdname != NULL) 240 strlcpy(bsdname, if_name(ifp), IFNAMSIZ); 241 NET_EPOCH_EXIT(et); 242 CURVNET_RESTORE(); 243 return (ifp != NULL ? 0 : EINVAL); 244 } 245 246 unsigned short 247 linux_ifflags(struct ifnet *ifp) 248 { 249 unsigned short flags; 250 251 NET_EPOCH_ASSERT(); 252 253 flags = if_getflags(ifp) | if_getdrvflags(ifp); 254 return (bsd_to_linux_ifflags(flags)); 255 } 256 257 unsigned short 258 bsd_to_linux_ifflags(int fl) 259 { 260 unsigned short flags = 0; 261 262 if (fl & IFF_UP) 263 flags |= LINUX_IFF_UP; 264 if (fl & IFF_BROADCAST) 265 flags |= LINUX_IFF_BROADCAST; 266 if (fl & IFF_DEBUG) 267 flags |= LINUX_IFF_DEBUG; 268 if (fl & IFF_LOOPBACK) 269 flags |= LINUX_IFF_LOOPBACK; 270 if (fl & IFF_POINTOPOINT) 271 flags |= LINUX_IFF_POINTOPOINT; 272 if (fl & IFF_DRV_RUNNING) 273 flags |= LINUX_IFF_RUNNING; 274 if (fl & IFF_NOARP) 275 flags |= LINUX_IFF_NOARP; 276 if (fl & IFF_PROMISC) 277 flags |= LINUX_IFF_PROMISC; 278 if (fl & IFF_ALLMULTI) 279 flags |= LINUX_IFF_ALLMULTI; 280 if (fl & IFF_MULTICAST) 281 flags |= LINUX_IFF_MULTICAST; 282 return (flags); 283 } 284 285 static u_int 286 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count) 287 { 288 struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; 289 struct l_sockaddr *lsa = arg; 290 291 if (count > 0) 292 return (0); 293 if (sdl->sdl_type != IFT_ETHER) 294 return (0); 295 bzero(lsa, sizeof(*lsa)); 296 lsa->sa_family = LINUX_ARPHRD_ETHER; 297 bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); 298 return (1); 299 } 300 301 int 302 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa) 303 { 304 305 NET_EPOCH_ASSERT(); 306 307 if (ifp->if_type == IFT_LOOP) { 308 bzero(lsa, sizeof(*lsa)); 309 lsa->sa_family = LINUX_ARPHRD_LOOPBACK; 310 return (0); 311 } 312 if (ifp->if_type != IFT_ETHER) 313 return (ENOENT); 314 if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) 315 return (0); 316 return (ENOENT); 317 } 318