1/*
2 * rt_names.c		rtnetlink names DB.
3 *
4 *		This program is free software; you can redistribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <fcntl.h>
17#include <string.h>
18#include <sys/time.h>
19#include <sys/socket.h>
20#include <dirent.h>
21#include <limits.h>
22
23#include <asm/types.h>
24#include <linux/rtnetlink.h>
25
26#include "rt_names.h"
27#include "utils.h"
28
29#define NAME_MAX_LEN 512
30
31struct rtnl_hash_entry {
32	struct rtnl_hash_entry	*next;
33	const char		*name;
34	unsigned int		id;
35};
36
37static int fread_id_name(FILE *fp, int *id, char *namebuf)
38{
39	char buf[NAME_MAX_LEN];
40
41	while (fgets(buf, sizeof(buf), fp)) {
42		char *p = buf;
43
44		while (*p == ' ' || *p == '\t')
45			p++;
46
47		if (*p == '#' || *p == '\n' || *p == 0)
48			continue;
49
50		if (sscanf(p, "0x%x %s\n", id, namebuf) != 2 &&
51				sscanf(p, "0x%x %s #", id, namebuf) != 2 &&
52				sscanf(p, "%d %s\n", id, namebuf) != 2 &&
53				sscanf(p, "%d %s #", id, namebuf) != 2) {
54			strcpy(namebuf, p);
55			return -1;
56		}
57		return 1;
58	}
59	return 0;
60}
61
62static void
63rtnl_hash_initialize(const char *file, struct rtnl_hash_entry **hash, int size)
64{
65	struct rtnl_hash_entry *entry;
66	FILE *fp;
67	int id;
68	char namebuf[NAME_MAX_LEN] = {0};
69	int ret;
70
71	fp = fopen(file, "r");
72	if (!fp)
73		return;
74
75	while ((ret = fread_id_name(fp, &id, &namebuf[0]))) {
76		if (ret == -1) {
77			fprintf(stderr, "Database %s is corrupted at %s\n",
78					file, namebuf);
79			fclose(fp);
80			return;
81		}
82
83		if (id < 0)
84			continue;
85
86		entry = malloc(sizeof(*entry));
87		entry->id   = id;
88		entry->name = strdup(namebuf);
89		entry->next = hash[id & (size - 1)];
90		hash[id & (size - 1)] = entry;
91	}
92	fclose(fp);
93}
94
95static void rtnl_tab_initialize(const char *file, char **tab, int size)
96{
97	FILE *fp;
98	int id;
99	char namebuf[NAME_MAX_LEN] = {0};
100	int ret;
101
102	fp = fopen(file, "r");
103	if (!fp)
104		return;
105
106	while ((ret = fread_id_name(fp, &id, &namebuf[0]))) {
107		if (ret == -1) {
108			fprintf(stderr, "Database %s is corrupted at %s\n",
109					file, namebuf);
110			fclose(fp);
111			return;
112		}
113		if (id < 0 || id > size)
114			continue;
115
116		tab[id] = strdup(namebuf);
117	}
118	fclose(fp);
119}
120
121static char *rtnl_rtprot_tab[256] = {
122	[RTPROT_UNSPEC]   = "unspec",
123	[RTPROT_REDIRECT] = "redirect",
124	[RTPROT_KERNEL]	  = "kernel",
125	[RTPROT_BOOT]	  = "boot",
126	[RTPROT_STATIC]	  = "static",
127
128	[RTPROT_GATED]	  = "gated",
129	[RTPROT_RA]	  = "ra",
130	[RTPROT_MRT]	  = "mrt",
131	[RTPROT_ZEBRA]	  = "zebra",
132	[RTPROT_BIRD]	  = "bird",
133	[RTPROT_BABEL]	  = "babel",
134	[RTPROT_DNROUTED] = "dnrouted",
135	[RTPROT_XORP]	  = "xorp",
136	[RTPROT_NTK]	  = "ntk",
137	[RTPROT_DHCP]	  = "dhcp",
138};
139
140
141static int rtnl_rtprot_init;
142
143static void rtnl_rtprot_initialize(void)
144{
145	struct dirent *de;
146	DIR *d;
147
148	rtnl_rtprot_init = 1;
149	rtnl_tab_initialize(CONFDIR "/rt_protos",
150			    rtnl_rtprot_tab, 256);
151
152	d = opendir(CONFDIR "/rt_protos.d");
153	if (!d)
154		return;
155
156	while ((de = readdir(d)) != NULL) {
157		char path[PATH_MAX];
158		size_t len;
159
160		if (*de->d_name == '.')
161			continue;
162
163		/* only consider filenames ending in '.conf' */
164		len = strlen(de->d_name);
165		if (len <= 5)
166			continue;
167		if (strcmp(de->d_name + len - 5, ".conf"))
168			continue;
169
170		snprintf(path, sizeof(path), CONFDIR "/rt_protos.d/%s",
171			 de->d_name);
172		rtnl_tab_initialize(path, rtnl_rtprot_tab, 256);
173	}
174	closedir(d);
175}
176
177const char *rtnl_rtprot_n2a(int id, char *buf, int len)
178{
179	if (id < 0 || id >= 256) {
180		snprintf(buf, len, "%u", id);
181		return buf;
182	}
183	if (!rtnl_rtprot_tab[id]) {
184		if (!rtnl_rtprot_init)
185			rtnl_rtprot_initialize();
186	}
187	if (rtnl_rtprot_tab[id])
188		return rtnl_rtprot_tab[id];
189	snprintf(buf, len, "%u", id);
190	return buf;
191}
192
193int rtnl_rtprot_a2n(__u32 *id, const char *arg)
194{
195	static char *cache;
196	static unsigned long res;
197	char *end;
198	int i;
199
200	if (cache && strcmp(cache, arg) == 0) {
201		*id = res;
202		return 0;
203	}
204
205	if (!rtnl_rtprot_init)
206		rtnl_rtprot_initialize();
207
208	for (i = 0; i < 256; i++) {
209		if (rtnl_rtprot_tab[i] &&
210		    strcmp(rtnl_rtprot_tab[i], arg) == 0) {
211			cache = rtnl_rtprot_tab[i];
212			res = i;
213			*id = res;
214			return 0;
215		}
216	}
217
218	res = strtoul(arg, &end, 0);
219	if (!end || end == arg || *end || res > 255)
220		return -1;
221	*id = res;
222	return 0;
223}
224
225
226static char *rtnl_rtscope_tab[256] = {
227	[RT_SCOPE_UNIVERSE]	= "global",
228	[RT_SCOPE_NOWHERE]	= "nowhere",
229	[RT_SCOPE_HOST]		= "host",
230	[RT_SCOPE_LINK]		= "link",
231	[RT_SCOPE_SITE]		= "site",
232};
233
234static int rtnl_rtscope_init;
235
236static void rtnl_rtscope_initialize(void)
237{
238	rtnl_rtscope_init = 1;
239	rtnl_tab_initialize(CONFDIR "/rt_scopes",
240			    rtnl_rtscope_tab, 256);
241}
242
243const char *rtnl_rtscope_n2a(int id, char *buf, int len)
244{
245	if (id < 0 || id >= 256) {
246		snprintf(buf, len, "%d", id);
247		return buf;
248	}
249
250	if (!rtnl_rtscope_tab[id]) {
251		if (!rtnl_rtscope_init)
252			rtnl_rtscope_initialize();
253	}
254
255	if (rtnl_rtscope_tab[id])
256		return rtnl_rtscope_tab[id];
257
258	snprintf(buf, len, "%d", id);
259	return buf;
260}
261
262int rtnl_rtscope_a2n(__u32 *id, const char *arg)
263{
264	static const char *cache;
265	static unsigned long res;
266	char *end;
267	int i;
268
269	if (cache && strcmp(cache, arg) == 0) {
270		*id = res;
271		return 0;
272	}
273
274	if (!rtnl_rtscope_init)
275		rtnl_rtscope_initialize();
276
277	for (i = 0; i < 256; i++) {
278		if (rtnl_rtscope_tab[i] &&
279		    strcmp(rtnl_rtscope_tab[i], arg) == 0) {
280			cache = rtnl_rtscope_tab[i];
281			res = i;
282			*id = res;
283			return 0;
284		}
285	}
286
287	res = strtoul(arg, &end, 0);
288	if (!end || end == arg || *end || res > 255)
289		return -1;
290	*id = res;
291	return 0;
292}
293
294
295static char *rtnl_rtrealm_tab[256] = {
296	"unknown",
297};
298
299static int rtnl_rtrealm_init;
300
301static void rtnl_rtrealm_initialize(void)
302{
303	rtnl_rtrealm_init = 1;
304	rtnl_tab_initialize(CONFDIR "/rt_realms",
305			    rtnl_rtrealm_tab, 256);
306}
307
308const char *rtnl_rtrealm_n2a(int id, char *buf, int len)
309{
310	if (id < 0 || id >= 256) {
311		snprintf(buf, len, "%d", id);
312		return buf;
313	}
314	if (!rtnl_rtrealm_tab[id]) {
315		if (!rtnl_rtrealm_init)
316			rtnl_rtrealm_initialize();
317	}
318	if (rtnl_rtrealm_tab[id])
319		return rtnl_rtrealm_tab[id];
320	snprintf(buf, len, "%d", id);
321	return buf;
322}
323
324
325int rtnl_rtrealm_a2n(__u32 *id, const char *arg)
326{
327	static char *cache;
328	static unsigned long res;
329	char *end;
330	int i;
331
332	if (cache && strcmp(cache, arg) == 0) {
333		*id = res;
334		return 0;
335	}
336
337	if (!rtnl_rtrealm_init)
338		rtnl_rtrealm_initialize();
339
340	for (i = 0; i < 256; i++) {
341		if (rtnl_rtrealm_tab[i] &&
342		    strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
343			cache = rtnl_rtrealm_tab[i];
344			res = i;
345			*id = res;
346			return 0;
347		}
348	}
349
350	res = strtoul(arg, &end, 0);
351	if (!end || end == arg || *end || res > 255)
352		return -1;
353	*id = res;
354	return 0;
355}
356
357
358static struct rtnl_hash_entry dflt_table_entry  = { .name = "default" };
359static struct rtnl_hash_entry main_table_entry  = { .name = "main" };
360static struct rtnl_hash_entry local_table_entry = { .name = "local" };
361
362static struct rtnl_hash_entry *rtnl_rttable_hash[256] = {
363	[RT_TABLE_DEFAULT] = &dflt_table_entry,
364	[RT_TABLE_MAIN]    = &main_table_entry,
365	[RT_TABLE_LOCAL]   = &local_table_entry,
366};
367
368static int rtnl_rttable_init;
369
370static void rtnl_rttable_initialize(void)
371{
372	struct dirent *de;
373	DIR *d;
374	int i;
375
376	rtnl_rttable_init = 1;
377	for (i = 0; i < 256; i++) {
378		if (rtnl_rttable_hash[i])
379			rtnl_rttable_hash[i]->id = i;
380	}
381	rtnl_hash_initialize(CONFDIR "/rt_tables",
382			     rtnl_rttable_hash, 256);
383
384	d = opendir(CONFDIR "/rt_tables.d");
385	if (!d)
386		return;
387
388	while ((de = readdir(d)) != NULL) {
389		char path[PATH_MAX];
390		size_t len;
391
392		if (*de->d_name == '.')
393			continue;
394
395		/* only consider filenames ending in '.conf' */
396		len = strlen(de->d_name);
397		if (len <= 5)
398			continue;
399		if (strcmp(de->d_name + len - 5, ".conf"))
400			continue;
401
402		snprintf(path, sizeof(path),
403			 CONFDIR "/rt_tables.d/%s", de->d_name);
404		rtnl_hash_initialize(path, rtnl_rttable_hash, 256);
405	}
406	closedir(d);
407}
408
409const char *rtnl_rttable_n2a(__u32 id, char *buf, int len)
410{
411	struct rtnl_hash_entry *entry;
412
413	if (!rtnl_rttable_init)
414		rtnl_rttable_initialize();
415	entry = rtnl_rttable_hash[id & 255];
416	while (entry && entry->id != id)
417		entry = entry->next;
418	if (entry)
419		return entry->name;
420	snprintf(buf, len, "%u", id);
421	return buf;
422}
423
424int rtnl_rttable_a2n(__u32 *id, const char *arg)
425{
426	static const char *cache;
427	static unsigned long res;
428	struct rtnl_hash_entry *entry;
429	char *end;
430	unsigned long i;
431
432	if (cache && strcmp(cache, arg) == 0) {
433		*id = res;
434		return 0;
435	}
436
437	if (!rtnl_rttable_init)
438		rtnl_rttable_initialize();
439
440	for (i = 0; i < 256; i++) {
441		entry = rtnl_rttable_hash[i];
442		while (entry && strcmp(entry->name, arg))
443			entry = entry->next;
444		if (entry) {
445			cache = entry->name;
446			res = entry->id;
447			*id = res;
448			return 0;
449		}
450	}
451
452	i = strtoul(arg, &end, 0);
453	if (!end || end == arg || *end || i > RT_TABLE_MAX)
454		return -1;
455	*id = i;
456	return 0;
457}
458
459
460static char *rtnl_rtdsfield_tab[256] = {
461	"0",
462};
463
464static int rtnl_rtdsfield_init;
465
466static void rtnl_rtdsfield_initialize(void)
467{
468	rtnl_rtdsfield_init = 1;
469	rtnl_tab_initialize(CONFDIR "/rt_dsfield",
470			    rtnl_rtdsfield_tab, 256);
471}
472
473const char *rtnl_dsfield_n2a(int id, char *buf, int len)
474{
475	if (id < 0 || id >= 256) {
476		snprintf(buf, len, "%d", id);
477		return buf;
478	}
479	if (!rtnl_rtdsfield_tab[id]) {
480		if (!rtnl_rtdsfield_init)
481			rtnl_rtdsfield_initialize();
482	}
483	if (rtnl_rtdsfield_tab[id])
484		return rtnl_rtdsfield_tab[id];
485	snprintf(buf, len, "0x%02x", id);
486	return buf;
487}
488
489
490int rtnl_dsfield_a2n(__u32 *id, const char *arg)
491{
492	static char *cache;
493	static unsigned long res;
494	char *end;
495	int i;
496
497	if (cache && strcmp(cache, arg) == 0) {
498		*id = res;
499		return 0;
500	}
501
502	if (!rtnl_rtdsfield_init)
503		rtnl_rtdsfield_initialize();
504
505	for (i = 0; i < 256; i++) {
506		if (rtnl_rtdsfield_tab[i] &&
507		    strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
508			cache = rtnl_rtdsfield_tab[i];
509			res = i;
510			*id = res;
511			return 0;
512		}
513	}
514
515	res = strtoul(arg, &end, 16);
516	if (!end || end == arg || *end || res > 255)
517		return -1;
518	*id = res;
519	return 0;
520}
521
522
523static struct rtnl_hash_entry dflt_group_entry = {
524	.id = 0, .name = "default"
525};
526
527static struct rtnl_hash_entry *rtnl_group_hash[256] = {
528	[0] = &dflt_group_entry,
529};
530
531static int rtnl_group_init;
532
533static void rtnl_group_initialize(void)
534{
535	rtnl_group_init = 1;
536	rtnl_hash_initialize(CONFDIR "/group",
537			     rtnl_group_hash, 256);
538}
539
540int rtnl_group_a2n(int *id, const char *arg)
541{
542	static const char *cache;
543	static unsigned long res;
544	struct rtnl_hash_entry *entry;
545	char *end;
546	int i;
547
548	if (cache && strcmp(cache, arg) == 0) {
549		*id = res;
550		return 0;
551	}
552
553	if (!rtnl_group_init)
554		rtnl_group_initialize();
555
556	for (i = 0; i < 256; i++) {
557		entry = rtnl_group_hash[i];
558		while (entry && strcmp(entry->name, arg))
559			entry = entry->next;
560		if (entry) {
561			cache = entry->name;
562			res = entry->id;
563			*id = res;
564			return 0;
565		}
566	}
567
568	i = strtol(arg, &end, 0);
569	if (!end || end == arg || *end || i < 0)
570		return -1;
571	*id = i;
572	return 0;
573}
574
575const char *rtnl_group_n2a(int id, char *buf, int len)
576{
577	struct rtnl_hash_entry *entry;
578	int i;
579
580	if (!rtnl_group_init)
581		rtnl_group_initialize();
582
583	for (i = 0; i < 256; i++) {
584		entry = rtnl_group_hash[i];
585
586		while (entry) {
587			if (entry->id == id)
588				return entry->name;
589			entry = entry->next;
590		}
591	}
592
593	snprintf(buf, len, "%d", id);
594	return buf;
595}
596
597static char *nl_proto_tab[256] = {
598	[NETLINK_ROUTE]          = "rtnl",
599	[NETLINK_UNUSED]         = "unused",
600	[NETLINK_USERSOCK]       = "usersock",
601	[NETLINK_FIREWALL]       = "fw",
602	[NETLINK_SOCK_DIAG]      = "tcpdiag",
603	[NETLINK_NFLOG]          = "nflog",
604	[NETLINK_XFRM]           = "xfrm",
605	[NETLINK_SELINUX]        = "selinux",
606	[NETLINK_ISCSI]          = "iscsi",
607	[NETLINK_AUDIT]          = "audit",
608	[NETLINK_FIB_LOOKUP]     = "fiblookup",
609	[NETLINK_CONNECTOR]      = "connector",
610	[NETLINK_NETFILTER]      = "nft",
611	[NETLINK_IP6_FW]         = "ip6fw",
612	[NETLINK_DNRTMSG]        = "dec-rt",
613	[NETLINK_KOBJECT_UEVENT] = "uevent",
614	[NETLINK_GENERIC]        = "genl",
615	[NETLINK_SCSITRANSPORT]  = "scsi-trans",
616	[NETLINK_ECRYPTFS]       = "ecryptfs",
617	[NETLINK_RDMA]           = "rdma",
618	[NETLINK_CRYPTO]         = "crypto",
619};
620
621static int nl_proto_init;
622
623static void nl_proto_initialize(void)
624{
625	nl_proto_init = 1;
626	rtnl_tab_initialize(CONFDIR "/nl_protos",
627			    nl_proto_tab, 256);
628}
629
630const char *nl_proto_n2a(int id, char *buf, int len)
631{
632	if (id < 0 || id >= 256) {
633		snprintf(buf, len, "%u", id);
634		return buf;
635	}
636
637	if (!nl_proto_init)
638		nl_proto_initialize();
639
640	if (nl_proto_tab[id])
641		return nl_proto_tab[id];
642
643	snprintf(buf, len, "%u", id);
644	return buf;
645}
646
647int nl_proto_a2n(__u32 *id, const char *arg)
648{
649	static char *cache;
650	static unsigned long res;
651	char *end;
652	int i;
653
654	if (cache && strcmp(cache, arg) == 0) {
655		*id = res;
656		return 0;
657	}
658
659	if (!nl_proto_init)
660		nl_proto_initialize();
661
662	for (i = 0; i < 256; i++) {
663		if (nl_proto_tab[i] &&
664		    strcmp(nl_proto_tab[i], arg) == 0) {
665			cache = nl_proto_tab[i];
666			res = i;
667			*id = res;
668			return 0;
669		}
670	}
671
672	res = strtoul(arg, &end, 0);
673	if (!end || end == arg || *end || res > 255)
674		return -1;
675	*id = res;
676	return 0;
677}
678