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