Commit | Line | Data |
---|---|---|
cb1c44c2 | 1 | /* |
1810611d | 2 | * Copyright (c) 1980, 1986 Regents of the University of California. |
5b519e94 | 3 | * All rights reserved. |
cb1c44c2 | 4 | * |
5b519e94 | 5 | * Redistribution and use in source and binary forms are permitted |
50c7758a KB |
6 | * provided that the above copyright notice and this paragraph are |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
5b519e94 | 16 | * |
65c6d60a | 17 | * @(#)if.c 7.8 (Berkeley) %G% |
cb1c44c2 | 18 | */ |
1bfd8df7 | 19 | |
a0369dcf | 20 | #include "param.h" |
ce355163 | 21 | #include "mbuf.h" |
a0369dcf JB |
22 | #include "systm.h" |
23 | #include "socket.h" | |
f211a0b7 | 24 | #include "socketvar.h" |
a0369dcf | 25 | #include "protosw.h" |
a0369dcf JB |
26 | #include "user.h" |
27 | #include "kernel.h" | |
28 | #include "ioctl.h" | |
29 | #include "errno.h" | |
30 | ||
31 | #include "if.h" | |
32 | #include "af.h" | |
1bfd8df7 | 33 | |
17fcfec5 MK |
34 | #include "ether.h" |
35 | ||
1e977657 BJ |
36 | int ifqmaxlen = IFQ_MAXLEN; |
37 | ||
ee787340 SL |
38 | /* |
39 | * Network interface utility routines. | |
40 | * | |
f211a0b7 MK |
41 | * Routines with ifa_ifwith* names take sockaddr *'s as |
42 | * parameters. | |
ee787340 SL |
43 | */ |
44 | ||
85ce71f2 BJ |
45 | ifinit() |
46 | { | |
47 | register struct ifnet *ifp; | |
48 | ||
49 | for (ifp = ifnet; ifp; ifp = ifp->if_next) | |
aad26eac MK |
50 | if (ifp->if_snd.ifq_maxlen == 0) |
51 | ifp->if_snd.ifq_maxlen = ifqmaxlen; | |
5248a70b | 52 | if_slowtimo(); |
85ce71f2 BJ |
53 | } |
54 | ||
a62dd253 | 55 | #ifdef vax |
ee787340 SL |
56 | /* |
57 | * Call each interface on a Unibus reset. | |
58 | */ | |
85ce71f2 BJ |
59 | ifubareset(uban) |
60 | int uban; | |
61 | { | |
62 | register struct ifnet *ifp; | |
63 | ||
64 | for (ifp = ifnet; ifp; ifp = ifp->if_next) | |
8af3ca7c | 65 | if (ifp->if_reset) |
9d6a72e7 | 66 | (*ifp->if_reset)(ifp->if_unit, uban); |
85ce71f2 | 67 | } |
14fa60f2 | 68 | #endif |
85ce71f2 | 69 | |
ee787340 SL |
70 | /* |
71 | * Attach an interface to the | |
72 | * list of "active" interfaces. | |
73 | */ | |
405c9168 BJ |
74 | if_attach(ifp) |
75 | struct ifnet *ifp; | |
76 | { | |
c4af8b24 | 77 | register struct ifnet **p = &ifnet; |
405c9168 | 78 | |
c4af8b24 BJ |
79 | while (*p) |
80 | p = &((*p)->if_next); | |
81 | *p = ifp; | |
405c9168 BJ |
82 | } |
83 | ||
ee787340 SL |
84 | /* |
85 | * Locate an interface based on a complete address. | |
86 | */ | |
4ad99bae | 87 | /*ARGSUSED*/ |
f211a0b7 MK |
88 | struct ifaddr * |
89 | ifa_ifwithaddr(addr) | |
202403f4 | 90 | register struct sockaddr *addr; |
1bfd8df7 BJ |
91 | { |
92 | register struct ifnet *ifp; | |
f211a0b7 | 93 | register struct ifaddr *ifa; |
1bfd8df7 | 94 | |
ee787340 | 95 | #define equal(a1, a2) \ |
202403f4 | 96 | (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) |
f211a0b7 MK |
97 | for (ifp = ifnet; ifp; ifp = ifp->if_next) |
98 | for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { | |
202403f4 | 99 | if (ifa->ifa_addr->sa_family != addr->sa_family) |
ee787340 | 100 | continue; |
202403f4 | 101 | if (equal(addr, ifa->ifa_addr)) |
f211a0b7 | 102 | return (ifa); |
ee787340 | 103 | if ((ifp->if_flags & IFF_BROADCAST) && |
f211a0b7 MK |
104 | equal(&ifa->ifa_broadaddr, addr)) |
105 | return (ifa); | |
ee787340 | 106 | } |
f211a0b7 | 107 | return ((struct ifaddr *)0); |
1bfd8df7 | 108 | } |
ae674e00 KS |
109 | /* |
110 | * Locate the point to point interface with a given destination address. | |
111 | */ | |
112 | /*ARGSUSED*/ | |
113 | struct ifaddr * | |
114 | ifa_ifwithdstaddr(addr) | |
202403f4 | 115 | register struct sockaddr *addr; |
ae674e00 KS |
116 | { |
117 | register struct ifnet *ifp; | |
118 | register struct ifaddr *ifa; | |
119 | ||
120 | for (ifp = ifnet; ifp; ifp = ifp->if_next) | |
121 | if (ifp->if_flags & IFF_POINTOPOINT) | |
122 | for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { | |
202403f4 | 123 | if (ifa->ifa_addr->sa_family != addr->sa_family) |
ae674e00 | 124 | continue; |
202403f4 | 125 | if (equal(addr, ifa->ifa_dstaddr)) |
ae674e00 KS |
126 | return (ifa); |
127 | } | |
128 | return ((struct ifaddr *)0); | |
129 | } | |
1bfd8df7 | 130 | |
ee787340 SL |
131 | /* |
132 | * Find an interface on a specific network. If many, choice | |
133 | * is first found. | |
134 | */ | |
f211a0b7 MK |
135 | struct ifaddr * |
136 | ifa_ifwithnet(addr) | |
202403f4 | 137 | struct sockaddr *addr; |
ee787340 SL |
138 | { |
139 | register struct ifnet *ifp; | |
f211a0b7 | 140 | register struct ifaddr *ifa; |
202403f4 KS |
141 | register char *cp, *cp2, *cp3; |
142 | register char *cplim; | |
143 | u_int af = addr->sa_family; | |
ee787340 | 144 | |
e65dcd4c SL |
145 | if (af >= AF_MAX) |
146 | return (0); | |
f211a0b7 MK |
147 | for (ifp = ifnet; ifp; ifp = ifp->if_next) |
148 | for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { | |
202403f4 | 149 | if (ifa->ifa_addr->sa_family != af || ifa->ifa_netmask == 0) |
ee787340 | 150 | continue; |
202403f4 KS |
151 | cp = addr->sa_data; |
152 | cp2 = ifa->ifa_addr->sa_data; | |
153 | cp3 = ifa->ifa_netmask->sa_data; | |
154 | cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; | |
155 | for (; cp3 < cplim; cp3++) | |
156 | if ((*cp++ ^ *cp2++) & *cp3) | |
157 | break; | |
158 | if (cp3 == cplim) | |
f211a0b7 | 159 | return (ifa); |
202403f4 | 160 | } |
f211a0b7 | 161 | return ((struct ifaddr *)0); |
1bfd8df7 BJ |
162 | } |
163 | ||
2612fc91 | 164 | #ifdef notdef |
ee787340 SL |
165 | /* |
166 | * Find an interface using a specific address family | |
167 | */ | |
f211a0b7 MK |
168 | struct ifaddr * |
169 | ifa_ifwithaf(af) | |
ee787340 | 170 | register int af; |
8a13b737 | 171 | { |
ee787340 | 172 | register struct ifnet *ifp; |
f211a0b7 | 173 | register struct ifaddr *ifa; |
8a13b737 | 174 | |
ee787340 | 175 | for (ifp = ifnet; ifp; ifp = ifp->if_next) |
f211a0b7 | 176 | for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) |
202403f4 | 177 | if (ifa->ifa_addr->sa_family == af) |
f211a0b7 MK |
178 | return (ifa); |
179 | return ((struct ifaddr *)0); | |
8a13b737 | 180 | } |
2612fc91 | 181 | #endif |
f1b2fa5b | 182 | |
72e4f44e SL |
183 | /* |
184 | * Mark an interface down and notify protocols of | |
185 | * the transition. | |
af0b24db | 186 | * NOTE: must be called at splnet or eqivalent. |
72e4f44e SL |
187 | */ |
188 | if_down(ifp) | |
189 | register struct ifnet *ifp; | |
190 | { | |
f211a0b7 | 191 | register struct ifaddr *ifa; |
5248a70b | 192 | |
72e4f44e | 193 | ifp->if_flags &= ~IFF_UP; |
f211a0b7 | 194 | for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) |
202403f4 | 195 | pfctlinput(PRC_IFDOWN, ifa->ifa_addr); |
ce355163 MK |
196 | if_qflush(&ifp->if_snd); |
197 | } | |
198 | ||
199 | /* | |
200 | * Flush an interface queue. | |
201 | */ | |
202 | if_qflush(ifq) | |
203 | register struct ifqueue *ifq; | |
204 | { | |
205 | register struct mbuf *m, *n; | |
206 | ||
207 | n = ifq->ifq_head; | |
208 | while (m = n) { | |
209 | n = m->m_act; | |
210 | m_freem(m); | |
211 | } | |
212 | ifq->ifq_head = 0; | |
213 | ifq->ifq_tail = 0; | |
214 | ifq->ifq_len = 0; | |
72e4f44e | 215 | } |
de602274 SL |
216 | |
217 | /* | |
218 | * Handle interface watchdog timer routines. Called | |
219 | * from softclock, we decrement timers (if set) and | |
220 | * call the appropriate interface routine on expiration. | |
221 | */ | |
222 | if_slowtimo() | |
223 | { | |
224 | register struct ifnet *ifp; | |
202403f4 | 225 | int s = splimp(); |
de602274 | 226 | |
af0b24db SL |
227 | for (ifp = ifnet; ifp; ifp = ifp->if_next) { |
228 | if (ifp->if_timer == 0 || --ifp->if_timer) | |
229 | continue; | |
230 | if (ifp->if_watchdog) | |
de602274 | 231 | (*ifp->if_watchdog)(ifp->if_unit); |
af0b24db | 232 | } |
202403f4 | 233 | splx(s); |
6e7edb25 | 234 | timeout(if_slowtimo, (caddr_t)0, hz / IFNET_SLOWHZ); |
de602274 | 235 | } |
0c33c832 SL |
236 | |
237 | /* | |
a62dd253 SL |
238 | * Map interface name to |
239 | * interface structure pointer. | |
0c33c832 | 240 | */ |
a62dd253 SL |
241 | struct ifnet * |
242 | ifunit(name) | |
243 | register char *name; | |
0c33c832 | 244 | { |
0c33c832 | 245 | register char *cp; |
a62dd253 SL |
246 | register struct ifnet *ifp; |
247 | int unit; | |
725d9baf MK |
248 | unsigned len; |
249 | char *ep, c; | |
0c33c832 | 250 | |
a62dd253 | 251 | for (cp = name; cp < name + IFNAMSIZ && *cp; cp++) |
0c33c832 SL |
252 | if (*cp >= '0' && *cp <= '9') |
253 | break; | |
a62dd253 SL |
254 | if (*cp == '\0' || cp == name + IFNAMSIZ) |
255 | return ((struct ifnet *)0); | |
725d9baf MK |
256 | /* |
257 | * Save first char of unit, and pointer to it, | |
258 | * so we can put a null there to avoid matching | |
259 | * initial substrings of interface names. | |
260 | */ | |
261 | len = cp - name + 1; | |
262 | c = *cp; | |
263 | ep = cp; | |
264 | for (unit = 0; *cp >= '0' && *cp <= '9'; ) | |
265 | unit = unit * 10 + *cp++ - '0'; | |
266 | *ep = 0; | |
0c33c832 | 267 | for (ifp = ifnet; ifp; ifp = ifp->if_next) { |
725d9baf | 268 | if (bcmp(ifp->if_name, name, len)) |
0c33c832 SL |
269 | continue; |
270 | if (unit == ifp->if_unit) | |
a62dd253 | 271 | break; |
0c33c832 | 272 | } |
725d9baf | 273 | *ep = c; |
a62dd253 SL |
274 | return (ifp); |
275 | } | |
276 | ||
277 | /* | |
278 | * Interface ioctls. | |
279 | */ | |
f211a0b7 MK |
280 | ifioctl(so, cmd, data) |
281 | struct socket *so; | |
a62dd253 SL |
282 | int cmd; |
283 | caddr_t data; | |
284 | { | |
285 | register struct ifnet *ifp; | |
286 | register struct ifreq *ifr; | |
06c16dfa | 287 | int error; |
0c33c832 | 288 | |
0c33c832 SL |
289 | switch (cmd) { |
290 | ||
a62dd253 | 291 | case SIOCGIFCONF: |
202403f4 | 292 | case OSIOCGIFCONF: |
a62dd253 | 293 | return (ifconf(cmd, data)); |
0c33c832 | 294 | |
799358ac | 295 | #if defined(INET) && NETHER > 0 |
17fcfec5 MK |
296 | case SIOCSARP: |
297 | case SIOCDARP: | |
06c16dfa KM |
298 | if (error = suser(u.u_cred, &u.u_acflag)) |
299 | return (error); | |
17fcfec5 MK |
300 | /* FALL THROUGH */ |
301 | case SIOCGARP: | |
202403f4 | 302 | case OSIOCGARP: |
17fcfec5 MK |
303 | return (arpioctl(cmd, data)); |
304 | #endif | |
a62dd253 SL |
305 | } |
306 | ifr = (struct ifreq *)data; | |
307 | ifp = ifunit(ifr->ifr_name); | |
308 | if (ifp == 0) | |
309 | return (ENXIO); | |
310 | switch (cmd) { | |
0c33c832 | 311 | |
0c33c832 SL |
312 | case SIOCGIFFLAGS: |
313 | ifr->ifr_flags = ifp->if_flags; | |
314 | break; | |
315 | ||
484ee22e MK |
316 | case SIOCGIFMETRIC: |
317 | ifr->ifr_metric = ifp->if_metric; | |
318 | break; | |
319 | ||
81889e84 | 320 | case SIOCSIFFLAGS: |
06c16dfa KM |
321 | if (error = suser(u.u_cred, &u.u_acflag)) |
322 | return (error); | |
81889e84 SL |
323 | if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) { |
324 | int s = splimp(); | |
325 | if_down(ifp); | |
326 | splx(s); | |
327 | } | |
f211a0b7 MK |
328 | ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | |
329 | (ifr->ifr_flags &~ IFF_CANTCHANGE); | |
aad26eac MK |
330 | if (ifp->if_ioctl) |
331 | (void) (*ifp->if_ioctl)(ifp, cmd, data); | |
81889e84 SL |
332 | break; |
333 | ||
484ee22e | 334 | case SIOCSIFMETRIC: |
06c16dfa KM |
335 | if (error = suser(u.u_cred, &u.u_acflag)) |
336 | return (error); | |
484ee22e MK |
337 | ifp->if_metric = ifr->ifr_metric; |
338 | break; | |
339 | ||
0c33c832 | 340 | default: |
f211a0b7 | 341 | if (so->so_proto == 0) |
a62dd253 | 342 | return (EOPNOTSUPP); |
202403f4 | 343 | #ifndef COMPAT_43 |
f211a0b7 MK |
344 | return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL, |
345 | cmd, data, ifp)); | |
202403f4 KS |
346 | #else |
347 | { | |
348 | int error, ocmd = cmd; | |
349 | ||
350 | switch (cmd) { | |
351 | ||
352 | case SIOCSIFDSTADDR: | |
353 | case SIOCSIFADDR: | |
354 | case SIOCSIFBRDADDR: | |
355 | case SIOCSIFNETMASK: | |
356 | #if BYTE_ORDER != BIG_ENDIAN | |
357 | if (ifr->ifr_addr.sa_family == 0 && | |
358 | ifr->ifr_addr.sa_len < 16) { | |
359 | ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len; | |
360 | ifr->ifr_addr.sa_len = 16; | |
361 | } | |
362 | #else | |
363 | if (ifr->ifr_addr.sa_len == 0) | |
364 | ifr->ifr_addr.sa_len = 16; | |
365 | #endif | |
366 | break; | |
367 | ||
368 | case OSIOCGIFADDR: | |
369 | cmd = SIOCGIFADDR; | |
370 | break; | |
371 | ||
372 | case OSIOCGIFDSTADDR: | |
373 | cmd = SIOCGIFDSTADDR; | |
374 | break; | |
375 | ||
376 | case OSIOCGIFBRDADDR: | |
377 | cmd = SIOCGIFBRDADDR; | |
378 | break; | |
379 | ||
380 | case OSIOCGIFNETMASK: | |
381 | cmd = SIOCGIFNETMASK; | |
382 | } | |
383 | error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL, | |
384 | cmd, data, ifp)); | |
385 | switch (ocmd) { | |
386 | ||
387 | case OSIOCGIFADDR: | |
388 | case OSIOCGIFDSTADDR: | |
389 | case OSIOCGIFBRDADDR: | |
390 | case OSIOCGIFNETMASK: | |
391 | *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family; | |
392 | } | |
393 | return (error); | |
394 | ||
395 | } | |
396 | #endif | |
0c33c832 SL |
397 | } |
398 | return (0); | |
399 | } | |
400 | ||
401 | /* | |
402 | * Return interface configuration | |
403 | * of system. List may be used | |
404 | * in later ioctl's (above) to get | |
405 | * other information. | |
406 | */ | |
9e9695c7 | 407 | /*ARGSUSED*/ |
0c33c832 SL |
408 | ifconf(cmd, data) |
409 | int cmd; | |
410 | caddr_t data; | |
411 | { | |
412 | register struct ifconf *ifc = (struct ifconf *)data; | |
413 | register struct ifnet *ifp = ifnet; | |
f211a0b7 | 414 | register struct ifaddr *ifa; |
9b956fa5 SL |
415 | register char *cp, *ep; |
416 | struct ifreq ifr, *ifrp; | |
0c33c832 SL |
417 | int space = ifc->ifc_len, error = 0; |
418 | ||
9b956fa5 SL |
419 | ifrp = ifc->ifc_req; |
420 | ep = ifr.ifr_name + sizeof (ifr.ifr_name) - 2; | |
0c33c832 | 421 | for (; space > sizeof (ifr) && ifp; ifp = ifp->if_next) { |
9b956fa5 SL |
422 | bcopy(ifp->if_name, ifr.ifr_name, sizeof (ifr.ifr_name) - 2); |
423 | for (cp = ifr.ifr_name; cp < ep && *cp; cp++) | |
0c33c832 | 424 | ; |
9b956fa5 | 425 | *cp++ = '0' + ifp->if_unit; *cp = '\0'; |
f211a0b7 MK |
426 | if ((ifa = ifp->if_addrlist) == 0) { |
427 | bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); | |
428 | error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); | |
429 | if (error) | |
430 | break; | |
431 | space -= sizeof (ifr), ifrp++; | |
432 | } else | |
433 | for ( ; space > sizeof (ifr) && ifa; ifa = ifa->ifa_next) { | |
202403f4 KS |
434 | register struct sockaddr *sa = ifa->ifa_addr; |
435 | #ifdef COMPAT_43 | |
436 | if (cmd == OSIOCGIFCONF) { | |
437 | struct osockaddr *osa = | |
438 | (struct osockaddr *)&ifr.ifr_addr; | |
439 | ifr.ifr_addr = *sa; | |
440 | osa->sa_family = sa->sa_family; | |
441 | error = copyout((caddr_t)&ifr, (caddr_t)ifrp, | |
442 | sizeof (ifr)); | |
443 | ifrp++; | |
444 | } else | |
445 | #endif | |
446 | if (sa->sa_len <= sizeof(*sa)) { | |
447 | ifr.ifr_addr = *sa; | |
448 | error = copyout((caddr_t)&ifr, (caddr_t)ifrp, | |
449 | sizeof (ifr)); | |
450 | ifrp++; | |
451 | } else { | |
452 | space -= sa->sa_len - sizeof(*sa); | |
453 | if (space < sizeof (ifr)) | |
454 | break; | |
455 | error = copyout((caddr_t)&ifr, (caddr_t)ifrp, | |
456 | sizeof (ifr.ifr_name)); | |
457 | if (error == 0) | |
458 | error = copyout((caddr_t)sa, | |
459 | (caddr_t)&ifrp->ifr_addr, sa->sa_len); | |
460 | ifrp = (struct ifreq *) | |
461 | (sa->sa_len + (caddr_t)&ifrp->ifr_addr); | |
462 | } | |
f211a0b7 MK |
463 | if (error) |
464 | break; | |
202403f4 | 465 | space -= sizeof (ifr); |
f211a0b7 | 466 | } |
0c33c832 SL |
467 | } |
468 | ifc->ifc_len -= space; | |
469 | return (error); | |
470 | } |