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