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