make changes to RTM_GET so that you can indicate that you want
[unix-history] / usr / src / sys / net / if.c
CommitLineData
cb1c44c2 1/*
1810611d 2 * Copyright (c) 1980, 1986 Regents of the University of California.
5b519e94 3 * All rights reserved.
cb1c44c2 4 *
dbf0c423 5 * %sccs.include.redist.c%
5b519e94 6 *
dbf0c423 7 * @(#)if.c 7.13 (Berkeley) %G%
cb1c44c2 8 */
1bfd8df7 9
a0369dcf 10#include "param.h"
ce355163 11#include "mbuf.h"
a0369dcf
JB
12#include "systm.h"
13#include "socket.h"
f211a0b7 14#include "socketvar.h"
a0369dcf 15#include "protosw.h"
a0369dcf
JB
16#include "user.h"
17#include "kernel.h"
18#include "ioctl.h"
19#include "errno.h"
20
21#include "if.h"
22#include "af.h"
02e54a34 23#include "if_dl.h"
d6bc9b28 24#include "if_types.h"
1bfd8df7 25
17fcfec5
MK
26#include "ether.h"
27
1e977657
BJ
28int ifqmaxlen = IFQ_MAXLEN;
29
ee787340
SL
30/*
31 * Network interface utility routines.
32 *
f211a0b7
MK
33 * Routines with ifa_ifwith* names take sockaddr *'s as
34 * parameters.
ee787340
SL
35 */
36
85ce71f2
BJ
37ifinit()
38{
39 register struct ifnet *ifp;
40
41 for (ifp = ifnet; ifp; ifp = ifp->if_next)
aad26eac
MK
42 if (ifp->if_snd.ifq_maxlen == 0)
43 ifp->if_snd.ifq_maxlen = ifqmaxlen;
5248a70b 44 if_slowtimo();
85ce71f2
BJ
45}
46
a62dd253 47#ifdef vax
ee787340
SL
48/*
49 * Call each interface on a Unibus reset.
50 */
85ce71f2
BJ
51ifubareset(uban)
52 int uban;
53{
54 register struct ifnet *ifp;
55
56 for (ifp = ifnet; ifp; ifp = ifp->if_next)
8af3ca7c 57 if (ifp->if_reset)
9d6a72e7 58 (*ifp->if_reset)(ifp->if_unit, uban);
85ce71f2 59}
14fa60f2 60#endif
85ce71f2 61
02e54a34 62int if_index = 0;
d6bc9b28 63struct ifaddr **ifnet_addrs;
ee787340
SL
64/*
65 * Attach an interface to the
66 * list of "active" interfaces.
67 */
405c9168
BJ
68if_attach(ifp)
69 struct ifnet *ifp;
70{
02e54a34
KS
71 unsigned socksize, ifasize;
72 int namelen, unitlen;
73 char workbuf[16];
d6bc9b28 74 register struct ifnet **p = &ifnet;
02e54a34
KS
75 register struct sockaddr_dl *sdl;
76 register struct ifaddr *ifa;
d6bc9b28
KS
77 static int if_indexlim = 8;
78 extern link_rtrequest(), ether_output();
405c9168 79
c4af8b24
BJ
80 while (*p)
81 p = &((*p)->if_next);
82 *p = ifp;
02e54a34 83 ifp->if_index = ++if_index;
d6bc9b28
KS
84 if (ifnet_addrs == 0 || if_index >= if_indexlim) {
85 unsigned n = (if_indexlim <<= 1) * sizeof(ifa);
86 struct ifaddr **q = (struct ifaddr **)
87 malloc(n, M_IFADDR, M_WAITOK);
88 if (ifnet_addrs) {
89 bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2);
90 free((caddr_t)ifnet_addrs, M_IFADDR);
91 }
92 ifnet_addrs = q;
93 }
94 /* XXX -- Temporary fix before changing 10 ethernet drivers */
95 if (ifp->if_output == ether_output) {
96 ifp->if_type = IFT_ETHER;
97 ifp->if_addrlen = 6;
98 ifp->if_hdrlen = 14;
99 }
f2eb28c8
KS
100 /*
101 * create a Link Level name for this device
102 */
02e54a34
KS
103 sprint_d(workbuf, ifp->if_unit);
104 namelen = strlen(ifp->if_name);
105 unitlen = strlen(workbuf);
106#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
107 socksize = _offsetof(struct sockaddr_dl, sdl_data[0]) +
108 unitlen + namelen + ifp->if_addrlen;
109#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
110 socksize = ROUNDUP(socksize);
d6bc9b28
KS
111 if (socksize < sizeof(*sdl))
112 socksize = sizeof(*sdl);
02e54a34
KS
113 ifasize = sizeof(*ifa) + 2 * socksize;
114 ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK);
115 if (ifa == 0)
116 return;
d6bc9b28 117 ifnet_addrs[if_index - 1] = ifa;
02e54a34
KS
118 bzero((caddr_t)ifa, ifasize);
119 sdl = (struct sockaddr_dl *)(ifa + 1);
120 ifa->ifa_addr = (struct sockaddr *)sdl;
121 ifa->ifa_ifp = ifp;
122 sdl->sdl_len = socksize;
123 sdl->sdl_family = AF_LINK;
124 bcopy(ifp->if_name, sdl->sdl_data, namelen);
125 bcopy((caddr_t)workbuf, namelen + (caddr_t)sdl->sdl_data, unitlen);
126 sdl->sdl_nlen = (namelen += unitlen);
127 sdl->sdl_index = ifp->if_index;
128 sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
129 ifa->ifa_netmask = (struct sockaddr *)sdl;
130 sdl->sdl_len = socksize - ifp->if_addrlen;
131 while (namelen != 0)
132 sdl->sdl_data[--namelen] = 0xff;
133 ifa->ifa_next = ifp->if_addrlist;
f2eb28c8 134 ifa->ifa_rtrequest = link_rtrequest;
02e54a34 135 ifp->if_addrlist = ifa;
405c9168 136}
ee787340
SL
137/*
138 * Locate an interface based on a complete address.
139 */
4ad99bae 140/*ARGSUSED*/
f211a0b7
MK
141struct ifaddr *
142ifa_ifwithaddr(addr)
202403f4 143 register struct sockaddr *addr;
1bfd8df7
BJ
144{
145 register struct ifnet *ifp;
f211a0b7 146 register struct ifaddr *ifa;
1bfd8df7 147
ee787340 148#define equal(a1, a2) \
202403f4 149 (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
f211a0b7
MK
150 for (ifp = ifnet; ifp; ifp = ifp->if_next)
151 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
202403f4 152 if (ifa->ifa_addr->sa_family != addr->sa_family)
ee787340 153 continue;
202403f4 154 if (equal(addr, ifa->ifa_addr))
f211a0b7 155 return (ifa);
d6bc9b28
KS
156 if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
157 equal(ifa->ifa_broadaddr, addr))
f211a0b7 158 return (ifa);
ee787340 159 }
f211a0b7 160 return ((struct ifaddr *)0);
1bfd8df7 161}
ae674e00
KS
162/*
163 * Locate the point to point interface with a given destination address.
164 */
165/*ARGSUSED*/
166struct ifaddr *
167ifa_ifwithdstaddr(addr)
202403f4 168 register struct sockaddr *addr;
ae674e00
KS
169{
170 register struct ifnet *ifp;
171 register struct ifaddr *ifa;
172
173 for (ifp = ifnet; ifp; ifp = ifp->if_next)
174 if (ifp->if_flags & IFF_POINTOPOINT)
175 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
202403f4 176 if (ifa->ifa_addr->sa_family != addr->sa_family)
ae674e00 177 continue;
202403f4 178 if (equal(addr, ifa->ifa_dstaddr))
ae674e00
KS
179 return (ifa);
180 }
181 return ((struct ifaddr *)0);
182}
1bfd8df7 183
ee787340
SL
184/*
185 * Find an interface on a specific network. If many, choice
186 * is first found.
187 */
f211a0b7
MK
188struct ifaddr *
189ifa_ifwithnet(addr)
202403f4 190 struct sockaddr *addr;
ee787340
SL
191{
192 register struct ifnet *ifp;
f211a0b7 193 register struct ifaddr *ifa;
202403f4 194 u_int af = addr->sa_family;
ee787340 195
e65dcd4c
SL
196 if (af >= AF_MAX)
197 return (0);
d6bc9b28
KS
198 if (af == AF_LINK) {
199 register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
200 if (sdl->sdl_index && sdl->sdl_index <= if_index)
201 return (ifnet_addrs[sdl->sdl_index - 1]);
202 }
f211a0b7
MK
203 for (ifp = ifnet; ifp; ifp = ifp->if_next)
204 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
d6bc9b28
KS
205 register char *cp, *cp2, *cp3;
206 register char *cplim;
202403f4 207 if (ifa->ifa_addr->sa_family != af || ifa->ifa_netmask == 0)
ee787340 208 continue;
202403f4
KS
209 cp = addr->sa_data;
210 cp2 = ifa->ifa_addr->sa_data;
211 cp3 = ifa->ifa_netmask->sa_data;
212 cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
213 for (; cp3 < cplim; cp3++)
214 if ((*cp++ ^ *cp2++) & *cp3)
215 break;
216 if (cp3 == cplim)
f211a0b7 217 return (ifa);
202403f4 218 }
f211a0b7 219 return ((struct ifaddr *)0);
1bfd8df7
BJ
220}
221
ee787340
SL
222/*
223 * Find an interface using a specific address family
224 */
f211a0b7
MK
225struct ifaddr *
226ifa_ifwithaf(af)
ee787340 227 register int af;
8a13b737 228{
ee787340 229 register struct ifnet *ifp;
f211a0b7 230 register struct ifaddr *ifa;
8a13b737 231
ee787340 232 for (ifp = ifnet; ifp; ifp = ifp->if_next)
f211a0b7 233 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
202403f4 234 if (ifa->ifa_addr->sa_family == af)
f211a0b7
MK
235 return (ifa);
236 return ((struct ifaddr *)0);
8a13b737 237}
f2eb28c8 238
d6bc9b28
KS
239/*
240 * Find an interface address specific to an interface best matching
241 * a given address.
242 */
243struct ifaddr *
244ifaof_ifpforaddr(addr, ifp)
245 struct sockaddr *addr;
246 register struct ifnet *ifp;
247{
248 register struct ifaddr *ifa;
249 register char *cp, *cp2, *cp3;
250 register char *cplim;
251 struct ifaddr *ifa_maybe = 0;
252 u_int af = addr->sa_family;
253
254 if (af >= AF_MAX)
255 return (0);
256 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
257 if (ifa->ifa_addr->sa_family != af)
258 continue;
259 ifa_maybe = ifa;
260 if (ifa->ifa_netmask == 0) {
261 if (equal(addr, ifa->ifa_addr) ||
262 (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
263 return (ifa);
264 continue;
265 }
266 cp = addr->sa_data;
267 cp2 = ifa->ifa_addr->sa_data;
268 cp3 = ifa->ifa_netmask->sa_data;
269 cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
270 for (; cp3 < cplim; cp3++)
271 if ((*cp++ ^ *cp2++) & *cp3)
272 break;
273 if (cp3 == cplim)
274 return (ifa);
275 }
276 return (ifa_maybe);
277}
f2eb28c8
KS
278#include "route.h"
279/*
280 * Default action when installing a route with a Link Level gateway.
281 * Lookup an appropriate real ifa to point to.
282 * This should be moved to /sys/net/link.c eventually.
283 */
284link_rtrequest(cmd, rt, sa)
285register struct rtentry *rt;
286struct sockaddr *sa;
287{
288 register struct ifaddr *ifa;
289 struct sockaddr *dst;
290 struct ifnet *ifp, *oldifnet = ifnet;
291
292 if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) ||
293 ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0))
294 return;
d6bc9b28 295 if (ifa = ifaof_ifpforaddr(dst, ifp)) {
f2eb28c8
KS
296 rt->rt_ifa = ifa;
297 if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest)
298 ifa->ifa_rtrequest(cmd, rt, sa);
d6bc9b28 299 }
f2eb28c8 300}
f1b2fa5b 301
72e4f44e
SL
302/*
303 * Mark an interface down and notify protocols of
304 * the transition.
af0b24db 305 * NOTE: must be called at splnet or eqivalent.
72e4f44e
SL
306 */
307if_down(ifp)
308 register struct ifnet *ifp;
309{
f211a0b7 310 register struct ifaddr *ifa;
5248a70b 311
72e4f44e 312 ifp->if_flags &= ~IFF_UP;
f211a0b7 313 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
202403f4 314 pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
ce355163
MK
315 if_qflush(&ifp->if_snd);
316}
317
318/*
319 * Flush an interface queue.
320 */
321if_qflush(ifq)
322 register struct ifqueue *ifq;
323{
324 register struct mbuf *m, *n;
325
326 n = ifq->ifq_head;
327 while (m = n) {
328 n = m->m_act;
329 m_freem(m);
330 }
331 ifq->ifq_head = 0;
332 ifq->ifq_tail = 0;
333 ifq->ifq_len = 0;
72e4f44e 334}
de602274
SL
335
336/*
337 * Handle interface watchdog timer routines. Called
338 * from softclock, we decrement timers (if set) and
339 * call the appropriate interface routine on expiration.
340 */
341if_slowtimo()
342{
343 register struct ifnet *ifp;
202403f4 344 int s = splimp();
de602274 345
af0b24db
SL
346 for (ifp = ifnet; ifp; ifp = ifp->if_next) {
347 if (ifp->if_timer == 0 || --ifp->if_timer)
348 continue;
349 if (ifp->if_watchdog)
de602274 350 (*ifp->if_watchdog)(ifp->if_unit);
af0b24db 351 }
202403f4 352 splx(s);
6e7edb25 353 timeout(if_slowtimo, (caddr_t)0, hz / IFNET_SLOWHZ);
de602274 354}
0c33c832
SL
355
356/*
a62dd253
SL
357 * Map interface name to
358 * interface structure pointer.
0c33c832 359 */
a62dd253
SL
360struct ifnet *
361ifunit(name)
362 register char *name;
0c33c832 363{
0c33c832 364 register char *cp;
a62dd253
SL
365 register struct ifnet *ifp;
366 int unit;
725d9baf
MK
367 unsigned len;
368 char *ep, c;
0c33c832 369
a62dd253 370 for (cp = name; cp < name + IFNAMSIZ && *cp; cp++)
0c33c832
SL
371 if (*cp >= '0' && *cp <= '9')
372 break;
a62dd253
SL
373 if (*cp == '\0' || cp == name + IFNAMSIZ)
374 return ((struct ifnet *)0);
725d9baf
MK
375 /*
376 * Save first char of unit, and pointer to it,
377 * so we can put a null there to avoid matching
378 * initial substrings of interface names.
379 */
380 len = cp - name + 1;
381 c = *cp;
382 ep = cp;
383 for (unit = 0; *cp >= '0' && *cp <= '9'; )
384 unit = unit * 10 + *cp++ - '0';
385 *ep = 0;
0c33c832 386 for (ifp = ifnet; ifp; ifp = ifp->if_next) {
725d9baf 387 if (bcmp(ifp->if_name, name, len))
0c33c832
SL
388 continue;
389 if (unit == ifp->if_unit)
a62dd253 390 break;
0c33c832 391 }
725d9baf 392 *ep = c;
a62dd253
SL
393 return (ifp);
394}
395
396/*
397 * Interface ioctls.
398 */
f211a0b7
MK
399ifioctl(so, cmd, data)
400 struct socket *so;
a62dd253
SL
401 int cmd;
402 caddr_t data;
403{
404 register struct ifnet *ifp;
405 register struct ifreq *ifr;
06c16dfa 406 int error;
0c33c832 407
0c33c832
SL
408 switch (cmd) {
409
a62dd253 410 case SIOCGIFCONF:
202403f4 411 case OSIOCGIFCONF:
a62dd253 412 return (ifconf(cmd, data));
0c33c832 413
799358ac 414#if defined(INET) && NETHER > 0
17fcfec5
MK
415 case SIOCSARP:
416 case SIOCDARP:
06c16dfa
KM
417 if (error = suser(u.u_cred, &u.u_acflag))
418 return (error);
17fcfec5
MK
419 /* FALL THROUGH */
420 case SIOCGARP:
202403f4 421 case OSIOCGARP:
17fcfec5
MK
422 return (arpioctl(cmd, data));
423#endif
a62dd253
SL
424 }
425 ifr = (struct ifreq *)data;
426 ifp = ifunit(ifr->ifr_name);
427 if (ifp == 0)
428 return (ENXIO);
429 switch (cmd) {
0c33c832 430
0c33c832
SL
431 case SIOCGIFFLAGS:
432 ifr->ifr_flags = ifp->if_flags;
433 break;
434
484ee22e
MK
435 case SIOCGIFMETRIC:
436 ifr->ifr_metric = ifp->if_metric;
437 break;
438
81889e84 439 case SIOCSIFFLAGS:
06c16dfa
KM
440 if (error = suser(u.u_cred, &u.u_acflag))
441 return (error);
81889e84
SL
442 if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) {
443 int s = splimp();
444 if_down(ifp);
445 splx(s);
446 }
f211a0b7
MK
447 ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
448 (ifr->ifr_flags &~ IFF_CANTCHANGE);
aad26eac
MK
449 if (ifp->if_ioctl)
450 (void) (*ifp->if_ioctl)(ifp, cmd, data);
81889e84
SL
451 break;
452
484ee22e 453 case SIOCSIFMETRIC:
06c16dfa
KM
454 if (error = suser(u.u_cred, &u.u_acflag))
455 return (error);
484ee22e
MK
456 ifp->if_metric = ifr->ifr_metric;
457 break;
458
0c33c832 459 default:
f211a0b7 460 if (so->so_proto == 0)
a62dd253 461 return (EOPNOTSUPP);
202403f4 462#ifndef COMPAT_43
f211a0b7
MK
463 return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
464 cmd, data, ifp));
202403f4
KS
465#else
466 {
35c34494 467 int ocmd = cmd;
202403f4
KS
468
469 switch (cmd) {
470
471 case SIOCSIFDSTADDR:
472 case SIOCSIFADDR:
473 case SIOCSIFBRDADDR:
474 case SIOCSIFNETMASK:
475#if BYTE_ORDER != BIG_ENDIAN
476 if (ifr->ifr_addr.sa_family == 0 &&
477 ifr->ifr_addr.sa_len < 16) {
478 ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len;
479 ifr->ifr_addr.sa_len = 16;
480 }
481#else
482 if (ifr->ifr_addr.sa_len == 0)
483 ifr->ifr_addr.sa_len = 16;
484#endif
485 break;
486
487 case OSIOCGIFADDR:
488 cmd = SIOCGIFADDR;
489 break;
490
491 case OSIOCGIFDSTADDR:
492 cmd = SIOCGIFDSTADDR;
493 break;
494
495 case OSIOCGIFBRDADDR:
496 cmd = SIOCGIFBRDADDR;
497 break;
498
499 case OSIOCGIFNETMASK:
500 cmd = SIOCGIFNETMASK;
501 }
502 error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL,
503 cmd, data, ifp));
504 switch (ocmd) {
505
506 case OSIOCGIFADDR:
507 case OSIOCGIFDSTADDR:
508 case OSIOCGIFBRDADDR:
509 case OSIOCGIFNETMASK:
510 *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family;
511 }
512 return (error);
513
514 }
515#endif
0c33c832
SL
516 }
517 return (0);
518}
519
520/*
521 * Return interface configuration
522 * of system. List may be used
523 * in later ioctl's (above) to get
524 * other information.
525 */
9e9695c7 526/*ARGSUSED*/
0c33c832
SL
527ifconf(cmd, data)
528 int cmd;
529 caddr_t data;
530{
531 register struct ifconf *ifc = (struct ifconf *)data;
532 register struct ifnet *ifp = ifnet;
f211a0b7 533 register struct ifaddr *ifa;
9b956fa5
SL
534 register char *cp, *ep;
535 struct ifreq ifr, *ifrp;
0c33c832
SL
536 int space = ifc->ifc_len, error = 0;
537
9b956fa5
SL
538 ifrp = ifc->ifc_req;
539 ep = ifr.ifr_name + sizeof (ifr.ifr_name) - 2;
0c33c832 540 for (; space > sizeof (ifr) && ifp; ifp = ifp->if_next) {
9b956fa5
SL
541 bcopy(ifp->if_name, ifr.ifr_name, sizeof (ifr.ifr_name) - 2);
542 for (cp = ifr.ifr_name; cp < ep && *cp; cp++)
0c33c832 543 ;
9b956fa5 544 *cp++ = '0' + ifp->if_unit; *cp = '\0';
f211a0b7
MK
545 if ((ifa = ifp->if_addrlist) == 0) {
546 bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
547 error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr));
548 if (error)
549 break;
550 space -= sizeof (ifr), ifrp++;
551 } else
552 for ( ; space > sizeof (ifr) && ifa; ifa = ifa->ifa_next) {
202403f4
KS
553 register struct sockaddr *sa = ifa->ifa_addr;
554#ifdef COMPAT_43
555 if (cmd == OSIOCGIFCONF) {
556 struct osockaddr *osa =
557 (struct osockaddr *)&ifr.ifr_addr;
558 ifr.ifr_addr = *sa;
559 osa->sa_family = sa->sa_family;
560 error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
561 sizeof (ifr));
562 ifrp++;
563 } else
564#endif
565 if (sa->sa_len <= sizeof(*sa)) {
566 ifr.ifr_addr = *sa;
567 error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
568 sizeof (ifr));
569 ifrp++;
570 } else {
571 space -= sa->sa_len - sizeof(*sa);
572 if (space < sizeof (ifr))
573 break;
574 error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
575 sizeof (ifr.ifr_name));
576 if (error == 0)
577 error = copyout((caddr_t)sa,
578 (caddr_t)&ifrp->ifr_addr, sa->sa_len);
579 ifrp = (struct ifreq *)
580 (sa->sa_len + (caddr_t)&ifrp->ifr_addr);
581 }
f211a0b7
MK
582 if (error)
583 break;
202403f4 584 space -= sizeof (ifr);
f211a0b7 585 }
0c33c832
SL
586 }
587 ifc->ifc_len -= space;
588 return (error);
589}
02e54a34
KS
590
591static sprint_d(cp, n)
592register char *cp;
593u_short n;
594{
595 register int q, m;
596 do {
597 if (n >= 10000) m = 10000;
598 else if (n >= 1000) m = 1000;
599 else if (n >= 100) m = 100;
600 else if (n >= 10) m = 10;
601 else m = 1;
602 q = n / m;
603 n -= m * q;
604 if (q > 9) q = 10; /* For crays with more than 100K interfaces */
605 *cp++ = "0123456789Z"[q];
606 } while (n > 0);
607 *cp++ = 0;
608}