#include <string.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
+#include <errno.h>
#include <asm/types.h>
#include <linux/rtnetlink.h>
return 0;
}
-static void
+static int
rtnl_hash_initialize(const char *file, struct rtnl_hash_entry **hash, int size)
{
struct rtnl_hash_entry *entry;
fp = fopen(file, "r");
if (!fp)
- return;
+ return -errno;
while ((ret = fread_id_name(fp, &id, &namebuf[0]))) {
if (ret == -1) {
fprintf(stderr, "Database %s is corrupted at %s\n",
file, namebuf);
fclose(fp);
- return;
+ return -EINVAL;
}
if (id < 0)
hash[id & (size - 1)] = entry;
}
fclose(fp);
+
+ return 0;
}
-static void rtnl_tab_initialize(const char *file, char **tab, int size)
+static int rtnl_tab_initialize(const char *file, char **tab, int size)
{
FILE *fp;
int id;
fp = fopen(file, "r");
if (!fp)
- return;
+ return -errno;
while ((ret = fread_id_name(fp, &id, &namebuf[0]))) {
if (ret == -1) {
fprintf(stderr, "Database %s is corrupted at %s\n",
file, namebuf);
fclose(fp);
- return;
+ return -EINVAL;
}
if (id < 0 || id > size)
continue;
tab[id] = strdup(namebuf);
}
fclose(fp);
+
+ return 0;
}
static char *rtnl_rtprot_tab[256] = {
[RTPROT_EIGRP] = "eigrp",
};
+struct tabhash {
+ enum { TAB, HASH } type;
+ union tab_or_hash {
+ char **tab;
+ struct rtnl_hash_entry **hash;
+ } data;
+};
-static int rtnl_rtprot_init;
-
-static void rtnl_rtprot_initialize(void)
+static void
+rtnl_tabhash_readdir(const char *dirpath_base, const char *dirpath_overload,
+ const struct tabhash tabhash, const int size)
{
struct dirent *de;
DIR *d;
- rtnl_rtprot_init = 1;
- rtnl_tab_initialize(CONFDIR "/rt_protos",
- rtnl_rtprot_tab, 256);
-
- d = opendir(CONFDIR "/rt_protos.d");
- if (!d)
- return;
-
- while ((de = readdir(d)) != NULL) {
+ d = opendir(dirpath_base);
+ while (d && (de = readdir(d)) != NULL) {
char path[PATH_MAX];
size_t len;
+ struct stat sb;
if (*de->d_name == '.')
continue;
if (strcmp(de->d_name + len - 5, ".conf"))
continue;
- snprintf(path, sizeof(path), CONFDIR "/rt_protos.d/%s",
- de->d_name);
- rtnl_tab_initialize(path, rtnl_rtprot_tab, 256);
+ if (dirpath_overload) {
+ /* only consider filenames not present in
+ the overloading directory, e.g. /etc */
+ snprintf(path, sizeof(path), "%s/%s", dirpath_overload, de->d_name);
+ if (lstat(path, &sb) == 0)
+ continue;
+ }
+
+ /* load the conf file in the base directory, e.g., /usr */
+ snprintf(path, sizeof(path), "%s/%s", dirpath_base, de->d_name);
+ if (tabhash.type == TAB)
+ rtnl_tab_initialize(path, tabhash.data.tab, size);
+ else
+ rtnl_hash_initialize(path, tabhash.data.hash, size);
}
- closedir(d);
+ if (d)
+ closedir(d);
+}
+
+static void
+rtnl_tabhash_initialize_dir(const char *ddir, const struct tabhash tabhash, const int size)
+{
+ char dirpath_usr[PATH_MAX], dirpath_etc[PATH_MAX];
+
+ snprintf(dirpath_usr, sizeof(dirpath_usr), "%s/%s", CONF_USR_DIR, ddir);
+ snprintf(dirpath_etc, sizeof(dirpath_etc), "%s/%s", CONF_ETC_DIR, ddir);
+
+ /* load /usr/lib/iproute2/foo.d/X conf files, unless /etc/iproute2/foo.d/X exists */
+ rtnl_tabhash_readdir(dirpath_usr, dirpath_etc, tabhash, size);
+
+ /* load /etc/iproute2/foo.d/X conf files */
+ rtnl_tabhash_readdir(dirpath_etc, NULL, tabhash, size);
+}
+
+static void
+rtnl_tab_initialize_dir(const char *ddir, char **tab, const int size) {
+ struct tabhash tab_data = {.type = TAB, .data.tab = tab};
+ rtnl_tabhash_initialize_dir(ddir, tab_data, size);
+}
+
+static void
+rtnl_hash_initialize_dir(const char *ddir, struct rtnl_hash_entry **hash,
+ const int size) {
+ struct tabhash hash_data = {.type = HASH, .data.hash = hash};
+ rtnl_tabhash_initialize_dir(ddir, hash_data, size);
+}
+
+static int rtnl_rtprot_init;
+
+static void rtnl_rtprot_initialize(void)
+{
+ int ret;
+
+ rtnl_rtprot_init = 1;
+ ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_protos",
+ rtnl_rtprot_tab, 256);
+ if (ret == -ENOENT)
+ rtnl_tab_initialize(CONF_USR_DIR "/rt_protos",
+ rtnl_rtprot_tab, 256);
+
+ rtnl_tab_initialize_dir("rt_protos.d", rtnl_rtprot_tab, 256);
}
const char *rtnl_rtprot_n2a(int id, char *buf, int len)
static void rtnl_addrprot_initialize(void)
{
- rtnl_tab_initialize(CONFDIR "/rt_addrprotos",
- rtnl_addrprot_tab,
- ARRAY_SIZE(rtnl_addrprot_tab));
+ int ret;
+
rtnl_addrprot_tab_initialized = true;
+
+ ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_addrprotos",
+ rtnl_addrprot_tab,
+ ARRAY_SIZE(rtnl_addrprot_tab));
+ if (ret == -ENOENT)
+ ret = rtnl_tab_initialize(CONF_USR_DIR "/rt_addrprotos",
+ rtnl_addrprot_tab,
+ ARRAY_SIZE(rtnl_addrprot_tab));
}
const char *rtnl_addrprot_n2a(__u8 id, char *buf, int len)
static void rtnl_rtscope_initialize(void)
{
+ int ret;
+
rtnl_rtscope_init = 1;
- rtnl_tab_initialize(CONFDIR "/rt_scopes",
- rtnl_rtscope_tab, 256);
+ ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_scopes",
+ rtnl_rtscope_tab, 256);
+ if (ret == -ENOENT)
+ rtnl_tab_initialize(CONF_USR_DIR "/rt_scopes",
+ rtnl_rtscope_tab, 256);
}
const char *rtnl_rtscope_n2a(int id, char *buf, int len)
static void rtnl_rtrealm_initialize(void)
{
+ int ret;
+
rtnl_rtrealm_init = 1;
- rtnl_tab_initialize(CONFDIR "/rt_realms",
- rtnl_rtrealm_tab, 256);
+ ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_realms",
+ rtnl_rtrealm_tab, 256);
+ if (ret == -ENOENT)
+ rtnl_tab_initialize(CONF_USR_DIR "/rt_realms",
+ rtnl_rtrealm_tab, 256);
}
const char *rtnl_rtrealm_n2a(int id, char *buf, int len)
static void rtnl_rttable_initialize(void)
{
- struct dirent *de;
- DIR *d;
int i;
+ int ret;
rtnl_rttable_init = 1;
for (i = 0; i < 256; i++) {
if (rtnl_rttable_hash[i])
rtnl_rttable_hash[i]->id = i;
}
- rtnl_hash_initialize(CONFDIR "/rt_tables",
- rtnl_rttable_hash, 256);
-
- d = opendir(CONFDIR "/rt_tables.d");
- if (!d)
- return;
-
- while ((de = readdir(d)) != NULL) {
- char path[PATH_MAX];
- size_t len;
-
- if (*de->d_name == '.')
- continue;
+ ret = rtnl_hash_initialize(CONF_ETC_DIR "/rt_tables",
+ rtnl_rttable_hash, 256);
+ if (ret == -ENOENT)
+ rtnl_hash_initialize(CONF_USR_DIR "/rt_tables",
+ rtnl_rttable_hash, 256);
- /* only consider filenames ending in '.conf' */
- len = strlen(de->d_name);
- if (len <= 5)
- continue;
- if (strcmp(de->d_name + len - 5, ".conf"))
- continue;
-
- snprintf(path, sizeof(path),
- CONFDIR "/rt_tables.d/%s", de->d_name);
- rtnl_hash_initialize(path, rtnl_rttable_hash, 256);
- }
- closedir(d);
+ rtnl_hash_initialize_dir("rt_tables.d", rtnl_rttable_hash, 256);
}
const char *rtnl_rttable_n2a(__u32 id, char *buf, int len)
static void rtnl_rtdsfield_initialize(void)
{
+ int ret;
+
rtnl_rtdsfield_init = 1;
- rtnl_tab_initialize(CONFDIR "/rt_dsfield",
- rtnl_rtdsfield_tab, 256);
+ ret = rtnl_tab_initialize(CONF_ETC_DIR "/rt_dsfield",
+ rtnl_rtdsfield_tab, 256);
+ if (ret == -ENOENT)
+ rtnl_tab_initialize(CONF_USR_DIR "/rt_dsfield",
+ rtnl_rtdsfield_tab, 256);
}
const char *rtnl_dsfield_n2a(int id, char *buf, int len)
static void rtnl_group_initialize(void)
{
+ int ret;
+
rtnl_group_init = 1;
- rtnl_hash_initialize(CONFDIR "/group",
- rtnl_group_hash, 256);
+ ret = rtnl_hash_initialize(CONF_ETC_DIR "/group",
+ rtnl_group_hash, 256);
+ if (ret == -ENOENT)
+ rtnl_hash_initialize(CONF_USR_DIR "/group",
+ rtnl_group_hash, 256);
}
int rtnl_group_a2n(int *id, const char *arg)
static void nl_proto_initialize(void)
{
+ int ret;
+
nl_proto_init = 1;
- rtnl_tab_initialize(CONFDIR "/nl_protos",
- nl_proto_tab, 256);
+ ret = rtnl_tab_initialize(CONF_ETC_DIR "/nl_protos",
+ nl_proto_tab, 256);
+ if (ret == -ENOENT)
+ rtnl_tab_initialize(CONF_USR_DIR "/nl_protos",
+ nl_proto_tab, 256);
}
const char *nl_proto_n2a(int id, char *buf, int len)
static void protodown_reason_initialize(void)
{
- struct dirent *de;
- DIR *d;
-
protodown_reason_init = 1;
- d = opendir(CONFDIR "/protodown_reasons.d");
- if (!d)
- return;
-
- while ((de = readdir(d)) != NULL) {
- char path[PATH_MAX];
- size_t len;
-
- if (*de->d_name == '.')
- continue;
-
- /* only consider filenames ending in '.conf' */
- len = strlen(de->d_name);
- if (len <= 5)
- continue;
- if (strcmp(de->d_name + len - 5, ".conf"))
- continue;
-
- snprintf(path, sizeof(path), CONFDIR "/protodown_reasons.d/%s",
- de->d_name);
- rtnl_tab_initialize(path, protodown_reason_tab,
- PROTODOWN_REASON_NUM_BITS);
- }
- closedir(d);
+ rtnl_tab_initialize_dir("protodown_reasons.d", protodown_reason_tab,
+ PROTODOWN_REASON_NUM_BITS);
}
int protodown_reason_n2a(int id, char *buf, int len)
.P
.B Route tables:
Linux-2.x can pack routes into several routing tables identified
-by a number in the range from 1 to 2^32-1 or by name from the file
-.B @SYSCONFDIR@/rt_tables
+by a number in the range from 1 to 2^32-1 or by name from
+.B @SYSCONF_USR_DIR@/rt_tables or
+.B @SYSCONF_ETC_DIR@/rt_tables (has precedence if exists).
By default all normal routes are inserted into the
.B main
table (ID 254) and the kernel only uses this table when calculating routes.
.I TOS
is either an 8 bit hexadecimal number or an identifier
from
-.BR "@SYSCONFDIR@/rt_dsfield" .
+.BR "@SYSCONF_USR_DIR@/rt_dsfield" or
+.BR "@SYSCONF_ETC_DIR@/rt_dsfield" (has precedence if exists).
.TP
.BI metric " NUMBER"
.BI table " TABLEID"
the table to add this route to.
.I TABLEID
-may be a number or a string from the file
-.BR "@SYSCONFDIR@/rt_tables" .
+may be a number or a string from
+.BR "@SYSCONF_USR_DIR@/rt_tables" or
+.BR "@SYSCONF_ETC_DIR@/rt_tables" (has precedence if exists).
If this parameter is omitted,
.B ip
assumes the
.BI realm " REALMID"
the realm to which this route is assigned.
.I REALMID
-may be a number or a string from the file
-.BR "@SYSCONFDIR@/rt_realms" .
+may be a number or a string from
+.BR "@SYSCONF_USR_DIR@/rt_realms" or
+.BR "@SYSCONF_ETC_DIR@/rt_realms" (has precedence if exists).
.TP
.BI mtu " MTU"
.BI scope " SCOPE_VAL"
the scope of the destinations covered by the route prefix.
.I SCOPE_VAL
-may be a number or a string from the file
-.BR "@SYSCONFDIR@/rt_scopes" .
+may be a number or a string from
+.BR "@SYSCONF_USR_DIR@/rt_scopes" or
+.BR "@SYSCONF_ETC_DIR@/rt_scopes" (has precedence if exists).
If this parameter is omitted,
.B ip
assumes scope
.BI protocol " RTPROTO"
the routing protocol identifier of this route.
.I RTPROTO
-may be a number or a string from the file
-.BR "@SYSCONFDIR@/rt_protos" .
+may be a number or a string from
+.BR "@SYSCONF_ETC_DIR@/rt_protos" or
+.BR "@SYSCONF_ETC_DIR@/rt_protos" (has precedence if exists).
If the routing protocol ID is not given,
.B ip assumes protocol
.B boot
- Decapsulate the inner IPv6 packet and forward it according to the
specified lookup table.
.I TABLEID
-is either a number or a string from the file
-.BR "@SYSCONFDIR@/rt_tables" .
+is either a number or a string from
+.BR "@SYSCONF_USR_DIR@/rt_tables" or
+.BR "@SYSCONF_ETC_DIR@/rt_tables" (has precedence if exists).
If
.B vrftable
is used, the argument must be a VRF device associated with
- Decapsulate the inner IPv4 packet and forward it according to the
specified lookup table.
.I TABLEID
-is either a number or a string from the file
-.BR "@SYSCONFDIR@/rt_tables" .
+is either a number or a string from
+.BR "@SYSCONF_USR_DIR@/rt_tables" or
+.BR "@SYSCONF_ETC_DIR@/rt_tables" (has precedence if exists).
The argument must be a VRF device associated with the table id.
Moreover, the VRF table associated with the table id must be configured
with the VRF strict mode turned on (net.vrf.strict_mode=1). This action
- Decapsulate the inner IPv4 or IPv6 packet and forward it according
to the specified lookup table.
.I TABLEID
-is either a number or a string from the file
-.BR "@SYSCONFDIR@/rt_tables" .
+is either a number or a string from
+.BR "@SYSCONF_USR_DIR@/rt_tables" or
+.BR "@SYSCONF_ETC_DIR@/rt_tables" (has precedence if exists).
The argument must be a VRF device associated with the table id.
Moreover, the VRF table associated with the table id must be configured
with the VRF strict mode turned on (net.vrf.strict_mode=1). This action