aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIan K. Coolidge <icoolidge@google.com>2020-05-27 11:03:45 -0700
committerDavid Ahern <dsahern@gmail.com>2020-05-31 23:01:22 +0000
commit9d59c86e575b5373d73f021f569ae520bc229ec5 (patch)
treee108a016a1a231b7e082224ba0a2b3fd29f36df7
parentbd4b8c632e92526e60bc18bd8453599ea78fa42f (diff)
downloadiproute2-9d59c86e575b5373d73f021f569ae520bc229ec5.tar.gz
iproute2: ip addr: Organize flag properties structurally
This creates a nice systematic way to check that the various flags are mutable from userspace and that the address family is valid. Mutability properties are preserved to avoid introducing any behavioral change in this CL. However, previously, immutable flags were ignored and fell through to this confusing error: Error: either "local" is duplicate, or "dadfailed" is a garbage. But now, they just warn more explicitly: Warning: dadfailed option is not mutable from userspace Signed-off-by: David Ahern <dsahern@gmail.com>
-rw-r--r--ip/ipaddress.c112
1 files changed, 55 insertions, 57 deletions
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 80d27ce27..403f70109 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -1233,52 +1233,63 @@ static unsigned int get_ifa_flags(struct ifaddrmsg *ifa,
ifa->ifa_flags;
}
-/* Mapping from argument to address flag mask */
-static const struct {
+/* Mapping from argument to address flag mask and attributes */
+static const struct ifa_flag_data_t {
const char *name;
- unsigned long value;
-} ifa_flag_names[] = {
- { "secondary", IFA_F_SECONDARY },
- { "temporary", IFA_F_SECONDARY },
- { "nodad", IFA_F_NODAD },
- { "optimistic", IFA_F_OPTIMISTIC },
- { "dadfailed", IFA_F_DADFAILED },
- { "home", IFA_F_HOMEADDRESS },
- { "deprecated", IFA_F_DEPRECATED },
- { "tentative", IFA_F_TENTATIVE },
- { "permanent", IFA_F_PERMANENT },
- { "mngtmpaddr", IFA_F_MANAGETEMPADDR },
- { "noprefixroute", IFA_F_NOPREFIXROUTE },
- { "autojoin", IFA_F_MCAUTOJOIN },
- { "stable-privacy", IFA_F_STABLE_PRIVACY },
+ unsigned long mask;
+ bool readonly;
+ bool v6only;
+} ifa_flag_data[] = {
+ { .name = "secondary", .mask = IFA_F_SECONDARY, .readonly = true, .v6only = false},
+ { .name = "temporary", .mask = IFA_F_SECONDARY, .readonly = true, .v6only = false},
+ { .name = "nodad", .mask = IFA_F_NODAD, .readonly = false, .v6only = true},
+ { .name = "optimistic", .mask = IFA_F_OPTIMISTIC, .readonly = true, .v6only = true},
+ { .name = "dadfailed", .mask = IFA_F_DADFAILED, .readonly = true, .v6only = true},
+ { .name = "home", .mask = IFA_F_HOMEADDRESS, .readonly = false, .v6only = true},
+ { .name = "deprecated", .mask = IFA_F_DEPRECATED, .readonly = true, .v6only = true},
+ { .name = "tentative", .mask = IFA_F_TENTATIVE, .readonly = true, .v6only = true},
+ { .name = "permanent", .mask = IFA_F_PERMANENT, .readonly = true, .v6only = true},
+ { .name = "mngtmpaddr", .mask = IFA_F_MANAGETEMPADDR, .readonly = false, .v6only = true},
+ { .name = "noprefixroute", .mask = IFA_F_NOPREFIXROUTE, .readonly = false, .v6only = true},
+ { .name = "autojoin", .mask = IFA_F_MCAUTOJOIN, .readonly = false, .v6only = true},
+ { .name = "stable-privacy", .mask = IFA_F_STABLE_PRIVACY, .readonly = true, .v6only = true},
};
+/* Returns a pointer to the data structure for a particular interface flag, or null if no flag could be found */
+static const struct ifa_flag_data_t* lookup_flag_data_by_name(const char* flag_name) {
+ for (int i = 0; i < ARRAY_SIZE(ifa_flag_data); ++i) {
+ if (strcmp(flag_name, ifa_flag_data[i].name) == 0)
+ return &ifa_flag_data[i];
+ }
+ return NULL;
+}
+
static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
unsigned int flags)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
- unsigned long mask = ifa_flag_names[i].value;
+ for (i = 0; i < ARRAY_SIZE(ifa_flag_data); i++) {
+ const struct ifa_flag_data_t* flag_data = &ifa_flag_data[i];
- if (mask == IFA_F_PERMANENT) {
- if (!(flags & mask))
+ if (flag_data->mask == IFA_F_PERMANENT) {
+ if (!(flags & flag_data->mask))
print_bool(PRINT_ANY,
"dynamic", "dynamic ", true);
- } else if (flags & mask) {
- if (mask == IFA_F_SECONDARY &&
+ } else if (flags & flag_data->mask) {
+ if (flag_data->mask == IFA_F_SECONDARY &&
ifa->ifa_family == AF_INET6) {
print_bool(PRINT_ANY,
"temporary", "temporary ", true);
} else {
print_string(PRINT_FP, NULL,
- "%s ", ifa_flag_names[i].name);
+ "%s ", flag_data->name);
print_bool(PRINT_JSON,
- ifa_flag_names[i].name, NULL, true);
+ flag_data->name, NULL, true);
}
}
- flags &= ~mask;
+ flags &= ~flag_data->mask;
}
if (flags) {
@@ -1297,7 +1308,6 @@ static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
static int get_filter(const char *arg)
{
bool inv = false;
- unsigned int i;
if (arg[0] == '-') {
inv = true;
@@ -1313,18 +1323,16 @@ static int get_filter(const char *arg)
arg = "secondary";
}
- for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
- if (strcmp(arg, ifa_flag_names[i].name))
- continue;
+ const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(arg);
+ if (flag_data == NULL)
+ return -1;
- if (inv)
- filter.flags &= ~ifa_flag_names[i].value;
- else
- filter.flags |= ifa_flag_names[i].value;
- filter.flagmask |= ifa_flag_names[i].value;
- return 0;
- }
- return -1;
+ if (inv)
+ filter.flags &= ~flag_data->mask;
+ else
+ filter.flags |= flag_data->mask;
+ filter.flagmask |= flag_data->mask;
+ return 0;
}
static int ifa_label_match_rta(int ifindex, const struct rtattr *rta)
@@ -2330,25 +2338,15 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
preferred_lftp = *argv;
if (set_lifetime(&preferred_lft, *argv))
invarg("preferred_lft value", *argv);
- } else if (strcmp(*argv, "home") == 0) {
- if (req.ifa.ifa_family == AF_INET6)
- ifa_flags |= IFA_F_HOMEADDRESS;
- else
- fprintf(stderr, "Warning: home option can be set only for IPv6 addresses\n");
- } else if (strcmp(*argv, "nodad") == 0) {
- if (req.ifa.ifa_family == AF_INET6)
- ifa_flags |= IFA_F_NODAD;
- else
- fprintf(stderr, "Warning: nodad option can be set only for IPv6 addresses\n");
- } else if (strcmp(*argv, "mngtmpaddr") == 0) {
- if (req.ifa.ifa_family == AF_INET6)
- ifa_flags |= IFA_F_MANAGETEMPADDR;
- else
- fprintf(stderr, "Warning: mngtmpaddr option can be set only for IPv6 addresses\n");
- } else if (strcmp(*argv, "noprefixroute") == 0) {
- ifa_flags |= IFA_F_NOPREFIXROUTE;
- } else if (strcmp(*argv, "autojoin") == 0) {
- ifa_flags |= IFA_F_MCAUTOJOIN;
+ } else if (lookup_flag_data_by_name(*argv)) {
+ const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(*argv);
+ if (flag_data->readonly) {
+ fprintf(stderr, "Warning: %s option is not mutable from userspace\n", flag_data->name);
+ } else if (flag_data->v6only && req.ifa.ifa_family != AF_INET6) {
+ fprintf(stderr, "Warning: %s option can be set only for IPv6 addresses\n", flag_data->name);
+ } else {
+ ifa_flags |= flag_data->mask;
+ }
} else {
if (strcmp(*argv, "local") == 0)
NEXT_ARG();