route.c revision 1591
1157299Smarcel/*
2157299Smarcel * Copyright (c) 1983, 1988, 1993
3157299Smarcel *	The Regents of the University of California.  All rights reserved.
4157299Smarcel *
5157299Smarcel * Redistribution and use in source and binary forms, with or without
6157299Smarcel * modification, are permitted provided that the following conditions
7157299Smarcel * are met:
8157299Smarcel * 1. Redistributions of source code must retain the above copyright
9157299Smarcel *    notice, this list of conditions and the following disclaimer.
10157299Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11157299Smarcel *    notice, this list of conditions and the following disclaimer in the
12157299Smarcel *    documentation and/or other materials provided with the distribution.
13157299Smarcel * 3. All advertising materials mentioning features or use of this software
14157299Smarcel *    must display the following acknowledgement:
15157299Smarcel *	This product includes software developed by the University of
16157299Smarcel *	California, Berkeley and its contributors.
17157299Smarcel * 4. Neither the name of the University nor the names of its contributors
18157299Smarcel *    may be used to endorse or promote products derived from this software
19157299Smarcel *    without specific prior written permission.
20157299Smarcel *
21157299Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22157299Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23157299Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24157299Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25157299Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26157299Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27157299Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28157299Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29157299Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30157299Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31157299Smarcel * SUCH DAMAGE.
32157299Smarcel */
33157299Smarcel
34157299Smarcel#ifndef lint
35157299Smarcelstatic char sccsid[] = "@(#)route.c	8.3 (Berkeley) 3/9/94";
36157299Smarcel#endif /* not lint */
37157299Smarcel
38157299Smarcel#include <sys/param.h>
39157299Smarcel#include <sys/protosw.h>
40157299Smarcel#include <sys/socket.h>
41157299Smarcel#include <sys/mbuf.h>
42157299Smarcel
43157299Smarcel#include <net/if.h>
44157299Smarcel#include <net/if_dl.h>
45157299Smarcel#include <net/if_types.h>
46157299Smarcel#define  KERNEL
47157299Smarcel#include <net/route.h>
48157299Smarcel#undef KERNEL
49157299Smarcel#include <netinet/in.h>
50157299Smarcel
51157299Smarcel#include <netns/ns.h>
52157299Smarcel
53157299Smarcel#include <sys/sysctl.h>
54157299Smarcel
55253899Smarius#include <netdb.h>
56157299Smarcel#include <stdio.h>
57157299Smarcel#include <stdlib.h>
58157299Smarcel#include <string.h>
59157299Smarcel#include <unistd.h>
60157299Smarcel#include "netstat.h"
61157299Smarcel
62157299Smarcel#define kget(p, d) (kread((u_long)(p), (char *)&(d), sizeof (d)))
63157299Smarcel
64157299Smarcel/*
65160689Smarcel * Definitions for showing gateway flags.
66157299Smarcel */
67157299Smarcelstruct bits {
68157299Smarcel	short	b_mask;
69157299Smarcel	char	b_val;
70157299Smarcel} bits[] = {
71157299Smarcel	{ RTF_UP,	'U' },
72157299Smarcel	{ RTF_GATEWAY,	'G' },
73157299Smarcel	{ RTF_HOST,	'H' },
74157299Smarcel	{ RTF_REJECT,	'R' },
75157299Smarcel	{ RTF_DYNAMIC,	'D' },
76157299Smarcel	{ RTF_MODIFIED,	'M' },
77157299Smarcel	{ RTF_DONE,	'd' }, /* Completed -- for routing messages only */
78157299Smarcel	{ RTF_MASK,	'm' }, /* Mask Present -- for routing messages only */
79253899Smarius	{ RTF_CLONING,	'C' },
80157299Smarcel	{ RTF_XRESOLVE,	'X' },
81157299Smarcel	{ RTF_LLINFO,	'L' },
82157299Smarcel	{ RTF_STATIC,	'S' },
83157299Smarcel	{ RTF_PROTO1,	'1' },
84157299Smarcel	{ RTF_PROTO2,	'2' },
85157299Smarcel	{ 0 }
86157299Smarcel};
87157299Smarcel
88157299Smarcelstatic union {
89157299Smarcel	struct	sockaddr u_sa;
90157299Smarcel	u_short	u_data[128];
91157299Smarcel} pt_u;
92157299Smarcel
93157299Smarcelint	do_rtent = 0;
94157299Smarcelstruct	rtentry rtentry;
95157299Smarcelstruct	radix_node rnode;
96157299Smarcelstruct	radix_mask rmask;
97157299Smarcel
98157299Smarcelint	NewTree = 0;
99157299Smarcel
100157299Smarcelstatic struct sockaddr *kgetsa __P((struct sockaddr *));
101157299Smarcelstatic void p_tree __P((struct radix_node *));
102157299Smarcelstatic void p_rtnode __P(());
103157299Smarcelstatic void ntreestuff __P(());
104157299Smarcelstatic void np_rtentry __P((struct rt_msghdr *));
105157299Smarcelstatic void p_sockaddr __P((struct sockaddr *, int, int));
106157299Smarcelstatic void p_flags __P((int, char *));
107157299Smarcelstatic void p_rtentry __P((struct rtentry *));
108157299Smarcel
109157299Smarcel/*
110157299Smarcel * Print routing tables.
111157299Smarcel */
112157299Smarcelvoid
113157299Smarcelroutepr(rtree)
114157299Smarcel	u_long rtree;
115157299Smarcel{
116157299Smarcel	struct radix_node_head *rnh, head;
117157299Smarcel	int i;
118157299Smarcel
119157299Smarcel	printf("Routing tables\n");
120157299Smarcel
121157299Smarcel	if (Aflag == 0 && NewTree)
122157299Smarcel		ntreestuff();
123157299Smarcel	else {
124157299Smarcel		if (rtree == 0) {
125157299Smarcel			printf("rt_tables: symbol not in namelist\n");
126157299Smarcel			return;
127157299Smarcel		}
128157299Smarcel
129157299Smarcel		kget(rtree, rt_tables);
130157299Smarcel		for (i = 0; i <= AF_MAX; i++) {
131157299Smarcel			if ((rnh = rt_tables[i]) == 0)
132157299Smarcel				continue;
133157299Smarcel			kget(rnh, head);
134157299Smarcel			if (i == AF_UNSPEC) {
135157299Smarcel				if (Aflag && af == 0) {
136157299Smarcel					printf("Netmasks:\n");
137157299Smarcel					p_tree(head.rnh_treetop);
138157299Smarcel				}
139157299Smarcel			} else if (af == AF_UNSPEC || af == i) {
140260287Sdim				pr_family(i);
141157299Smarcel				do_rtent = 1;
142157299Smarcel				pr_rthdr();
143157299Smarcel				p_tree(head.rnh_treetop);
144157299Smarcel			}
145157299Smarcel		}
146157299Smarcel	}
147157299Smarcel}
148157299Smarcel
149157299Smarcel/*
150157299Smarcel * Print address family header before a section of the routing table.
151157299Smarcel */
152157299Smarcelvoid
153157299Smarcelpr_family(af)
154157299Smarcel	int af;
155157299Smarcel{
156260287Sdim	char *afname;
157157299Smarcel
158157299Smarcel	switch (af) {
159157299Smarcel	case AF_INET:
160157299Smarcel		afname = "Internet";
161157299Smarcel		break;
162157299Smarcel	case AF_NS:
163157299Smarcel		afname = "XNS";
164157299Smarcel		break;
165157299Smarcel	case AF_ISO:
166157299Smarcel		afname = "ISO";
167157299Smarcel		break;
168157299Smarcel	case AF_CCITT:
169157299Smarcel		afname = "X.25";
170157299Smarcel		break;
171157299Smarcel	default:
172157299Smarcel		afname = NULL;
173157299Smarcel		break;
174157299Smarcel	}
175157299Smarcel	if (afname)
176157299Smarcel		printf("\n%s:\n", afname);
177253899Smarius	else
178157299Smarcel		printf("\nProtocol Family %d:\n", af);
179157299Smarcel}
180157299Smarcel
181157299Smarcel/* column widths; each followed by one space */
182#define	WID_DST		16	/* width of destination column */
183#define	WID_GW		18	/* width of gateway column */
184
185/*
186 * Print header for routing table columns.
187 */
188void
189pr_rthdr()
190{
191
192	if (Aflag)
193		printf("%-8.8s ","Address");
194	printf("%-*.*s %-*.*s %-6.6s  %6.6s%8.8s  %s\n",
195		WID_DST, WID_DST, "Destination",
196		WID_GW, WID_GW, "Gateway",
197		"Flags", "Refs", "Use", "Interface");
198}
199
200static struct sockaddr *
201kgetsa(dst)
202	register struct sockaddr *dst;
203{
204
205	kget(dst, pt_u.u_sa);
206	if (pt_u.u_sa.sa_len > sizeof (pt_u.u_sa))
207		kread((u_long)dst, (char *)pt_u.u_data, pt_u.u_sa.sa_len);
208	return (&pt_u.u_sa);
209}
210
211static void
212p_tree(rn)
213	struct radix_node *rn;
214{
215
216again:
217	kget(rn, rnode);
218	if (rnode.rn_b < 0) {
219		if (Aflag)
220			printf("%-8.8x ", rn);
221		if (rnode.rn_flags & RNF_ROOT) {
222			if (Aflag)
223				printf("(root node)%s",
224				    rnode.rn_dupedkey ? " =>\n" : "\n");
225		} else if (do_rtent) {
226			kget(rn, rtentry);
227			p_rtentry(&rtentry);
228			if (Aflag)
229				p_rtnode();
230		} else {
231			p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_key),
232			    0, 44);
233			putchar('\n');
234		}
235		if (rn = rnode.rn_dupedkey)
236			goto again;
237	} else {
238		if (Aflag && do_rtent) {
239			printf("%-8.8x ", rn);
240			p_rtnode();
241		}
242		rn = rnode.rn_r;
243		p_tree(rnode.rn_l);
244		p_tree(rn);
245	}
246}
247
248char	nbuf[20];
249
250static void
251p_rtnode()
252{
253	struct radix_mask *rm = rnode.rn_mklist;
254
255	if (rnode.rn_b < 0) {
256		if (rnode.rn_mask) {
257			printf("\t  mask ");
258			p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_mask),
259				    0, -1);
260		} else if (rm == 0)
261			return;
262	} else {
263		sprintf(nbuf, "(%d)", rnode.rn_b);
264		printf("%6.6s %8.8x : %8.8x", nbuf, rnode.rn_l, rnode.rn_r);
265	}
266	while (rm) {
267		kget(rm, rmask);
268		sprintf(nbuf, " %d refs, ", rmask.rm_refs);
269		printf(" mk = %8.8x {(%d),%s",
270			rm, -1 - rmask.rm_b, rmask.rm_refs ? nbuf : " ");
271		p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask), 0, -1);
272		putchar('}');
273		if (rm = rmask.rm_mklist)
274			printf(" ->");
275	}
276	putchar('\n');
277}
278
279static void
280ntreestuff()
281{
282	size_t needed;
283	int mib[6];
284	char *buf, *next, *lim;
285	register struct rt_msghdr *rtm;
286
287        mib[0] = CTL_NET;
288        mib[1] = PF_ROUTE;
289        mib[2] = 0;
290        mib[3] = 0;
291        mib[4] = NET_RT_DUMP;
292        mib[5] = 0;
293        if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
294		{ perror("route-sysctl-estimate"); exit(1);}
295	if ((buf = malloc(needed)) == 0)
296		{ printf("out of space\n"); exit(1);}
297        if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
298		{ perror("sysctl of routing table"); exit(1);}
299	lim  = buf + needed;
300	for (next = buf; next < lim; next += rtm->rtm_msglen) {
301		rtm = (struct rt_msghdr *)next;
302		np_rtentry(rtm);
303	}
304}
305
306static void
307np_rtentry(rtm)
308	register struct rt_msghdr *rtm;
309{
310	register struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
311#ifdef notdef
312	static int masks_done, banner_printed;
313#endif
314	static int old_af;
315	int af = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST;
316
317#ifdef notdef
318	/* for the moment, netmasks are skipped over */
319	if (!banner_printed) {
320		printf("Netmasks:\n");
321		banner_printed = 1;
322	}
323	if (masks_done == 0) {
324		if (rtm->rtm_addrs != RTA_DST ) {
325			masks_done = 1;
326			af = sa->sa_family;
327		}
328	} else
329#endif
330		af = sa->sa_family;
331	if (af != old_af) {
332		pr_family(af);
333		old_af = af;
334	}
335	if (rtm->rtm_addrs == RTA_DST)
336		p_sockaddr(sa, 0, 36);
337	else {
338		p_sockaddr(sa, rtm->rtm_flags, 16);
339		if (sa->sa_len == 0)
340			sa->sa_len = sizeof(long);
341		sa = (struct sockaddr *)(sa->sa_len + (char *)sa);
342		p_sockaddr(sa, 0, 18);
343	}
344	p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
345	putchar('\n');
346}
347
348static void
349p_sockaddr(sa, flags, width)
350	struct sockaddr *sa;
351	int flags, width;
352{
353	char workbuf[128], *cplim;
354	register char *cp = workbuf;
355
356	switch(sa->sa_family) {
357	case AF_INET:
358	    {
359		register struct sockaddr_in *sin = (struct sockaddr_in *)sa;
360
361		cp = (sin->sin_addr.s_addr == 0) ? "default" :
362		      ((flags & RTF_HOST) ?
363			routename(sin->sin_addr.s_addr) :
364			netname(sin->sin_addr.s_addr, 0L));
365		break;
366	    }
367
368	case AF_NS:
369		cp = ns_print(sa);
370		break;
371
372	case AF_LINK:
373	    {
374		register struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
375
376		if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
377		    sdl->sdl_slen == 0)
378			(void) sprintf(workbuf, "link#%d", sdl->sdl_index);
379		else switch (sdl->sdl_type) {
380		case IFT_ETHER:
381		    {
382			register int i;
383			register u_char *lla = (u_char *)sdl->sdl_data +
384			    sdl->sdl_nlen;
385
386			cplim = "";
387			for (i = 0; i < sdl->sdl_alen; i++, lla++) {
388				cp += sprintf(cp, "%s%x", cplim, *lla);
389				cplim = ":";
390			}
391			cp = workbuf;
392			break;
393		    }
394		default:
395			cp = link_ntoa(sdl);
396			break;
397		}
398		break;
399	    }
400
401	default:
402	    {
403		register u_char *s = (u_char *)sa->sa_data, *slim;
404
405		slim =  sa->sa_len + (u_char *) sa;
406		cplim = cp + sizeof(workbuf) - 6;
407		cp += sprintf(cp, "(%d)", sa->sa_family);
408		while (s < slim && cp < cplim) {
409			cp += sprintf(cp, " %02x", *s++);
410			if (s < slim)
411			    cp += sprintf(cp, "%02x", *s++);
412		}
413		cp = workbuf;
414	    }
415	}
416	if (width < 0 )
417		printf("%s ", cp);
418	else {
419		if (nflag)
420			printf("%-*s ", width, cp);
421		else
422			printf("%-*.*s ", width, width, cp);
423	}
424}
425
426static void
427p_flags(f, format)
428	register int f;
429	char *format;
430{
431	char name[33], *flags;
432	register struct bits *p = bits;
433
434	for (flags = name; p->b_mask; p++)
435		if (p->b_mask & f)
436			*flags++ = p->b_val;
437	*flags = '\0';
438	printf(format, name);
439}
440
441static void
442p_rtentry(rt)
443	register struct rtentry *rt;
444{
445	static struct ifnet ifnet, *lastif;
446	static char name[16];
447
448	p_sockaddr(kgetsa(rt_key(rt)), rt->rt_flags, WID_DST);
449	p_sockaddr(kgetsa(rt->rt_gateway), RTF_HOST, WID_GW);
450	p_flags(rt->rt_flags, "%-6.6s ");
451	printf("%6d %8d ", rt->rt_refcnt, rt->rt_use);
452	if (rt->rt_ifp) {
453		if (rt->rt_ifp != lastif) {
454			kget(rt->rt_ifp, ifnet);
455			kread((u_long)ifnet.if_name, name, 16);
456			lastif = rt->rt_ifp;
457		}
458		printf(" %.15s%d%s", name, ifnet.if_unit,
459			rt->rt_nodes[0].rn_dupedkey ? " =>" : "");
460	}
461	putchar('\n');
462}
463
464char *
465routename(in)
466	u_long in;
467{
468	register char *cp;
469	static char line[MAXHOSTNAMELEN + 1];
470	struct hostent *hp;
471	static char domain[MAXHOSTNAMELEN + 1];
472	static int first = 1;
473
474	if (first) {
475		first = 0;
476		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
477		    (cp = index(domain, '.')))
478			(void) strcpy(domain, cp + 1);
479		else
480			domain[0] = 0;
481	}
482	cp = 0;
483	if (!nflag) {
484		hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
485			AF_INET);
486		if (hp) {
487			if ((cp = index(hp->h_name, '.')) &&
488			    !strcmp(cp + 1, domain))
489				*cp = 0;
490			cp = hp->h_name;
491		}
492	}
493	if (cp)
494		strncpy(line, cp, sizeof(line) - 1);
495	else {
496#define C(x)	((x) & 0xff)
497		in = ntohl(in);
498		sprintf(line, "%u.%u.%u.%u",
499		    C(in >> 24), C(in >> 16), C(in >> 8), C(in));
500	}
501	return (line);
502}
503
504/*
505 * Return the name of the network whose address is given.
506 * The address is assumed to be that of a net or subnet, not a host.
507 */
508char *
509netname(in, mask)
510	u_long in, mask;
511{
512	char *cp = 0;
513	static char line[MAXHOSTNAMELEN + 1];
514	struct netent *np = 0;
515	u_long net;
516	register int i;
517	int subnetshift;
518
519	i = ntohl(in);
520	if (!nflag && i) {
521		if (mask == 0) {
522			if (IN_CLASSA(i)) {
523				mask = IN_CLASSA_NET;
524				subnetshift = 8;
525			} else if (IN_CLASSB(i)) {
526				mask = IN_CLASSB_NET;
527				subnetshift = 8;
528			} else {
529				mask = IN_CLASSC_NET;
530				subnetshift = 4;
531			}
532			/*
533			 * If there are more bits than the standard mask
534			 * would suggest, subnets must be in use.
535			 * Guess at the subnet mask, assuming reasonable
536			 * width subnet fields.
537			 */
538			while (i &~ mask)
539				mask = (long)mask >> subnetshift;
540		}
541		net = i & mask;
542		while ((mask & 1) == 0)
543			mask >>= 1, net >>= 1;
544		np = getnetbyaddr(net, AF_INET);
545		if (np)
546			cp = np->n_name;
547	}
548	if (cp)
549		strncpy(line, cp, sizeof(line) - 1);
550	else if ((i & 0xffffff) == 0)
551		sprintf(line, "%u", C(i >> 24));
552	else if ((i & 0xffff) == 0)
553		sprintf(line, "%u.%u", C(i >> 24) , C(i >> 16));
554	else if ((i & 0xff) == 0)
555		sprintf(line, "%u.%u.%u", C(i >> 24), C(i >> 16), C(i >> 8));
556	else
557		sprintf(line, "%u.%u.%u.%u", C(i >> 24),
558			C(i >> 16), C(i >> 8), C(i));
559	return (line);
560}
561
562/*
563 * Print routing statistics
564 */
565void
566rt_stats(off)
567	u_long off;
568{
569	struct rtstat rtstat;
570
571	if (off == 0) {
572		printf("rtstat: symbol not in namelist\n");
573		return;
574	}
575	kread(off, (char *)&rtstat, sizeof (rtstat));
576	printf("routing:\n");
577	printf("\t%u bad routing redirect%s\n",
578		rtstat.rts_badredirect, plural(rtstat.rts_badredirect));
579	printf("\t%u dynamically created route%s\n",
580		rtstat.rts_dynamic, plural(rtstat.rts_dynamic));
581	printf("\t%u new gateway%s due to redirects\n",
582		rtstat.rts_newgateway, plural(rtstat.rts_newgateway));
583	printf("\t%u destination%s found unreachable\n",
584		rtstat.rts_unreach, plural(rtstat.rts_unreach));
585	printf("\t%u use%s of a wildcard route\n",
586		rtstat.rts_wildcard, plural(rtstat.rts_wildcard));
587}
588short ns_nullh[] = {0,0,0};
589short ns_bh[] = {-1,-1,-1};
590
591char *
592ns_print(sa)
593	register struct sockaddr *sa;
594{
595	register struct sockaddr_ns *sns = (struct sockaddr_ns*)sa;
596	struct ns_addr work;
597	union { union ns_net net_e; u_long long_e; } net;
598	u_short port;
599	static char mybuf[50], cport[10], chost[25];
600	char *host = "";
601	register char *p; register u_char *q;
602
603	work = sns->sns_addr;
604	port = ntohs(work.x_port);
605	work.x_port = 0;
606	net.net_e  = work.x_net;
607	if (ns_nullhost(work) && net.long_e == 0) {
608		if (port ) {
609			sprintf(mybuf, "*.%xH", port);
610			upHex(mybuf);
611		} else
612			sprintf(mybuf, "*.*");
613		return (mybuf);
614	}
615
616	if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) {
617		host = "any";
618	} else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) {
619		host = "*";
620	} else {
621		q = work.x_host.c_host;
622		sprintf(chost, "%02x%02x%02x%02x%02x%02xH",
623			q[0], q[1], q[2], q[3], q[4], q[5]);
624		for (p = chost; *p == '0' && p < chost + 12; p++)
625			continue;
626		host = p;
627	}
628	if (port)
629		sprintf(cport, ".%xH", htons(port));
630	else
631		*cport = 0;
632
633	sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport);
634	upHex(mybuf);
635	return(mybuf);
636}
637
638char *
639ns_phost(sa)
640	struct sockaddr *sa;
641{
642	register struct sockaddr_ns *sns = (struct sockaddr_ns *)sa;
643	struct sockaddr_ns work;
644	static union ns_net ns_zeronet;
645	char *p;
646
647	work = *sns;
648	work.sns_addr.x_port = 0;
649	work.sns_addr.x_net = ns_zeronet;
650
651	p = ns_print((struct sockaddr *)&work);
652	if (strncmp("0H.", p, 3) == 0) p += 3;
653	return(p);
654}
655
656void
657upHex(p0)
658	char *p0;
659{
660	register char *p = p0;
661	for (; *p; p++) switch (*p) {
662
663	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
664		*p += ('A' - 'a');
665	}
666}
667