1 /* 2 * Copyright 2007, Hugo Santos. All Rights Reserved. 3 * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved. 4 * Copyright 2004, Marcus Overhagen. All Rights Reserved. 5 * 6 * Distributed under the terms of the MIT License. 7 */ 8 9 10 #include "device.h" 11 12 #include <stdio.h> 13 #include <net/if_types.h> 14 #include <sys/sockio.h> 15 16 #include <compat/sys/bus.h> 17 #include <compat/sys/kernel.h> 18 19 #include <compat/net/if.h> 20 #include <compat/net/if_arp.h> 21 #include <compat/net/if_var.h> 22 #include <compat/sys/malloc.h> 23 24 #include <compat/net/ethernet.h> 25 26 27 struct ifnet * 28 if_alloc(u_char type) 29 { 30 char semName[64]; 31 32 struct ifnet *ifp = _kernel_malloc(sizeof(struct ifnet), M_ZERO); 33 if (ifp == NULL) 34 return NULL; 35 36 snprintf(semName, sizeof(semName), "%s receive", gDriverName); 37 38 ifp->receive_sem = create_sem(0, semName); 39 if (ifp->receive_sem < B_OK) 40 goto err1; 41 42 if (type == IFT_ETHER) { 43 ifp->if_l2com = _kernel_malloc(sizeof(struct arpcom), M_ZERO); 44 if (ifp->if_l2com == NULL) 45 goto err2; 46 47 IFP2AC(ifp)->ac_ifp = ifp; 48 } 49 50 ifp->link_state_sem = -1; 51 ifp->open_count = 0; 52 ifp->flags = 0; 53 ifq_init(&ifp->receive_queue, semName); 54 55 ifp->if_type = type; 56 IF_ADDR_LOCK_INIT(ifp); 57 return ifp; 58 59 err2: 60 delete_sem(ifp->receive_sem); 61 err1: 62 _kernel_free(ifp); 63 return NULL; 64 } 65 66 67 void 68 if_free(struct ifnet *ifp) 69 { 70 IF_ADDR_LOCK_DESTROY(ifp); 71 if (ifp->if_type == IFT_ETHER) 72 _kernel_free(ifp->if_l2com); 73 74 delete_sem(ifp->receive_sem); 75 ifq_uninit(&ifp->receive_queue); 76 77 _kernel_free(ifp); 78 } 79 80 81 void 82 if_initname(struct ifnet *ifp, const char *name, int unit) 83 { 84 dprintf("if_initname(%p, %s, %d)\n", ifp, name, unit); 85 86 if (name == NULL) 87 panic("interface goes unamed"); 88 89 if (gDeviceCount >= MAX_DEVICES) 90 panic("unit too large"); 91 92 ifp->if_dname = name; 93 ifp->if_dunit = unit; 94 ifp->if_index = gDeviceCount++; 95 96 strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname)); 97 98 snprintf(ifp->device_name, sizeof(ifp->device_name), "net/%s/%i", 99 gDriverName, ifp->if_index); 100 101 driver_printf("%s: /dev/%s\n", gDriverName, ifp->device_name); 102 103 gDeviceNameList[ifp->if_index] = ifp->device_name; 104 gDevices[ifp->if_index] = ifp; 105 106 ifp->root_device = find_root_device(unit); 107 } 108 109 110 void 111 ifq_init(struct ifqueue *ifq, const char *name) 112 { 113 ifq->ifq_head = NULL; 114 ifq->ifq_tail = NULL; 115 ifq->ifq_len = 0; 116 ifq->ifq_maxlen = IFQ_MAXLEN; 117 ifq->ifq_drops = 0; 118 119 mtx_init(&ifq->ifq_mtx, name, NULL, MTX_DEF); 120 } 121 122 123 void 124 ifq_uninit(struct ifqueue *ifq) 125 { 126 mtx_destroy(&ifq->ifq_mtx); 127 } 128 129 130 void 131 if_attach(struct ifnet *ifp) 132 { 133 TAILQ_INIT(&ifp->if_addrhead); 134 TAILQ_INIT(&ifp->if_prefixhead); 135 TAILQ_INIT(&ifp->if_multiaddrs); 136 137 IF_ADDR_LOCK_INIT(ifp); 138 139 ifq_init((struct ifqueue *)&ifp->if_snd, ifp->if_xname); 140 } 141 142 143 void 144 if_detach(struct ifnet *ifp) 145 { 146 IF_ADDR_LOCK_DESTROY(ifp); 147 ifq_uninit((struct ifqueue *)&ifp->if_snd); 148 } 149 150 151 void 152 if_start(struct ifnet *ifp) 153 { 154 #ifdef IFF_NEEDSGIANT 155 if (ifp->if_flags & IFF_NEEDSGIANT) 156 panic("freebsd compat.: unsupported giant requirement"); 157 #endif 158 ifp->if_start(ifp); 159 } 160 161 162 int 163 if_printf(struct ifnet *ifp, const char *format, ...) 164 { 165 char buf[256]; 166 va_list vl; 167 va_start(vl, format); 168 vsnprintf(buf, sizeof(buf), format, vl); 169 va_end(vl); 170 171 dprintf("[%s] %s", ifp->device_name, buf); 172 return 0; 173 } 174 175 176 void 177 if_link_state_change(struct ifnet *ifp, int linkState) 178 { 179 if (ifp->if_link_state == linkState) 180 return; 181 182 ifp->if_link_state = linkState; 183 release_sem_etc(ifp->link_state_sem, 1, B_DO_NOT_RESCHEDULE); 184 } 185 186 187 static struct ifmultiaddr * 188 if_findmulti(struct ifnet *ifp, struct sockaddr *_address) 189 { 190 struct sockaddr_dl *address = (struct sockaddr_dl *)_address; 191 struct ifmultiaddr *ifma; 192 193 TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) { 194 if (memcmp(LLADDR(address), 195 LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 196 ETHER_ADDR_LEN) == 0) 197 return ifma; 198 } 199 200 return NULL; 201 } 202 203 204 static struct ifmultiaddr * 205 _if_addmulti(struct ifnet *ifp, struct sockaddr *address) 206 { 207 struct ifmultiaddr *addr = if_findmulti(ifp, address); 208 209 if (addr != NULL) { 210 addr->ifma_refcount++; 211 return addr; 212 } 213 214 addr = (struct ifmultiaddr *)malloc(sizeof(struct ifmultiaddr)); 215 if (addr == NULL) 216 return NULL; 217 218 addr->ifma_lladdr = NULL; 219 addr->ifma_ifp = ifp; 220 addr->ifma_protospec = NULL; 221 222 memcpy(&addr->ifma_addr_storage, address, sizeof(struct sockaddr_dl)); 223 addr->ifma_addr = (struct sockaddr *)&addr->ifma_addr_storage; 224 225 addr->ifma_refcount = 1; 226 227 TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, addr, ifma_link); 228 229 return addr; 230 } 231 232 233 int 234 if_addmulti(struct ifnet *ifp, struct sockaddr *address, 235 struct ifmultiaddr **out) 236 { 237 struct ifmultiaddr *result; 238 int refcount = 0; 239 240 IF_ADDR_LOCK(ifp); 241 result = _if_addmulti(ifp, address); 242 if (result) 243 refcount = result->ifma_refcount; 244 IF_ADDR_UNLOCK(ifp); 245 246 if (result == NULL) 247 return ENOBUFS; 248 249 if (refcount == 1) 250 ifp->if_ioctl(ifp, SIOCADDMULTI, NULL); 251 252 if (out) 253 (*out) = result; 254 255 return 0; 256 } 257 258 259 static void 260 if_delete_multiaddr(struct ifnet *ifp, struct ifmultiaddr *ifma) 261 { 262 TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); 263 free(ifma); 264 } 265 266 267 int 268 if_delmulti(struct ifnet *ifp, struct sockaddr *address) 269 { 270 struct ifmultiaddr *addr; 271 int deleted = 0; 272 273 IF_ADDR_LOCK(ifp); 274 addr = if_findmulti(ifp, address); 275 if (addr != NULL) { 276 addr->ifma_refcount--; 277 if (addr->ifma_refcount == 0) { 278 if_delete_multiaddr(ifp, addr); 279 deleted = 1; 280 } 281 } 282 IF_ADDR_UNLOCK(ifp); 283 284 if (deleted) 285 ifp->if_ioctl(ifp, SIOCDELMULTI, NULL); 286 287 return 0; 288 } 289 290 291 int 292 ether_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 293 struct rtentry *rt0) 294 { 295 int error = 0; 296 IFQ_HANDOFF(ifp, m, error); 297 return error; 298 } 299 300 301 static void 302 ether_input(struct ifnet *ifp, struct mbuf *m) 303 { 304 IF_ENQUEUE(&ifp->receive_queue, m); 305 release_sem_etc(ifp->receive_sem, 1, B_DO_NOT_RESCHEDULE); 306 } 307 308 309 void 310 ether_ifattach(struct ifnet *ifp, const uint8_t *macAddress) 311 { 312 ifp->if_addrlen = ETHER_ADDR_LEN; 313 ifp->if_hdrlen = ETHER_HDR_LEN; 314 if_attach(ifp); 315 ifp->if_mtu = ETHERMTU; 316 ifp->if_output = ether_output; 317 ifp->if_input = ether_input; 318 ifp->if_resolvemulti = NULL; /* done in the stack */ 319 320 ifp->if_lladdr.sdl_family = AF_LINK; 321 memcpy(IF_LLADDR(ifp), macAddress, ETHER_ADDR_LEN); 322 323 // TODO: according to FreeBSD's if_ethersubr.c, this should be removed 324 // once all drivers are cleaned up. 325 if (macAddress != IFP2ENADDR(ifp)) 326 memcpy(IFP2ENADDR(ifp), macAddress, ETHER_ADDR_LEN); 327 } 328 329 330 void 331 ether_ifdetach(struct ifnet *ifp) 332 { 333 if_detach(ifp); 334 } 335 336 337 int 338 ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 339 { 340 struct ifreq *ifr = (struct ifreq *)data; 341 342 switch (command) { 343 case SIOCSIFMTU: 344 if (ifr->ifr_mtu > ETHERMTU) 345 return EINVAL; 346 else 347 ; 348 /* need to fix our ifreq to work with C... */ 349 /* ifp->ifr_mtu = ifr->ifr_mtu; */ 350 break; 351 352 default: 353 return EINVAL; 354 } 355 356 return 0; 357 } 358 359