iproute2/lib/ll_map.c
<<
>>
Prefs
   1/*
   2 * ll_map.c
   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
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <unistd.h>
  16#include <fcntl.h>
  17#include <sys/socket.h>
  18#include <netinet/in.h>
  19#include <string.h>
  20#include <net/if.h>
  21
  22#include "libnetlink.h"
  23#include "ll_map.h"
  24#include "list.h"
  25#include "utils.h"
  26
  27struct ll_cache {
  28        struct hlist_node idx_hash;
  29        struct hlist_node name_hash;
  30        unsigned        flags;
  31        unsigned        index;
  32        unsigned short  type;
  33        struct list_head altnames_list;
  34        char            name[];
  35};
  36
  37#define IDXMAP_SIZE     1024
  38static struct hlist_head idx_head[IDXMAP_SIZE];
  39static struct hlist_head name_head[IDXMAP_SIZE];
  40
  41static struct ll_cache *ll_get_by_index(unsigned index)
  42{
  43        struct hlist_node *n;
  44        unsigned h = index & (IDXMAP_SIZE - 1);
  45
  46        hlist_for_each(n, &idx_head[h]) {
  47                struct ll_cache *im
  48                        = container_of(n, struct ll_cache, idx_hash);
  49                if (im->index == index)
  50                        return im;
  51        }
  52
  53        return NULL;
  54}
  55
  56unsigned namehash(const char *str)
  57{
  58        unsigned hash = 5381;
  59
  60        while (*str)
  61                hash = ((hash << 5) + hash) + *str++; /* hash * 33 + c */
  62
  63        return hash;
  64}
  65
  66static struct ll_cache *ll_get_by_name(const char *name)
  67{
  68        struct hlist_node *n;
  69        unsigned h = namehash(name) & (IDXMAP_SIZE - 1);
  70
  71        hlist_for_each(n, &name_head[h]) {
  72                struct ll_cache *im
  73                        = container_of(n, struct ll_cache, name_hash);
  74
  75                if (strcmp(im->name, name) == 0)
  76                        return im;
  77        }
  78
  79        return NULL;
  80}
  81
  82static struct ll_cache *ll_entry_create(struct ifinfomsg *ifi,
  83                                        const char *ifname,
  84                                        struct ll_cache *parent_im)
  85{
  86        struct ll_cache *im;
  87        unsigned int h;
  88
  89        im = malloc(sizeof(*im) + strlen(ifname) + 1);
  90        if (!im)
  91                return NULL;
  92        im->index = ifi->ifi_index;
  93        strcpy(im->name, ifname);
  94        im->type = ifi->ifi_type;
  95        im->flags = ifi->ifi_flags;
  96
  97        if (parent_im) {
  98                list_add_tail(&im->altnames_list, &parent_im->altnames_list);
  99        } else {
 100                /* This is parent, insert to index hash. */
 101                h = ifi->ifi_index & (IDXMAP_SIZE - 1);
 102                hlist_add_head(&im->idx_hash, &idx_head[h]);
 103                INIT_LIST_HEAD(&im->altnames_list);
 104        }
 105
 106        h = namehash(ifname) & (IDXMAP_SIZE - 1);
 107        hlist_add_head(&im->name_hash, &name_head[h]);
 108        return im;
 109}
 110
 111static void ll_entry_destroy(struct ll_cache *im, bool im_is_parent)
 112{
 113        hlist_del(&im->name_hash);
 114        if (im_is_parent)
 115                hlist_del(&im->idx_hash);
 116        else
 117                list_del(&im->altnames_list);
 118        free(im);
 119}
 120
 121static void ll_entry_update(struct ll_cache *im, struct ifinfomsg *ifi,
 122                            const char *ifname)
 123{
 124        unsigned int h;
 125
 126        im->flags = ifi->ifi_flags;
 127        if (!strcmp(im->name, ifname))
 128                return;
 129        hlist_del(&im->name_hash);
 130        h = namehash(ifname) & (IDXMAP_SIZE - 1);
 131        hlist_add_head(&im->name_hash, &name_head[h]);
 132}
 133
 134static void ll_altname_entries_create(struct ll_cache *parent_im,
 135                                      struct ifinfomsg *ifi, struct rtattr **tb)
 136{
 137        struct rtattr *i, *proplist = tb[IFLA_PROP_LIST];
 138        int rem;
 139
 140        if (!proplist)
 141                return;
 142        rem = RTA_PAYLOAD(proplist);
 143        for (i = RTA_DATA(proplist); RTA_OK(i, rem);
 144             i = RTA_NEXT(i, rem)) {
 145                if (i->rta_type != IFLA_ALT_IFNAME)
 146                        continue;
 147                ll_entry_create(ifi, rta_getattr_str(i), parent_im);
 148        }
 149}
 150
 151static void ll_altname_entries_destroy(struct ll_cache *parent_im)
 152{
 153        struct ll_cache *im, *tmp;
 154
 155        list_for_each_entry_safe(im, tmp, &parent_im->altnames_list,
 156                                 altnames_list)
 157                ll_entry_destroy(im, false);
 158}
 159
 160static void ll_altname_entries_update(struct ll_cache *parent_im,
 161                                      struct ifinfomsg *ifi, struct rtattr **tb)
 162{
 163        struct rtattr *i, *proplist = tb[IFLA_PROP_LIST];
 164        struct ll_cache *im;
 165        int rem;
 166
 167        if (!proplist) {
 168                ll_altname_entries_destroy(parent_im);
 169                return;
 170        }
 171
 172        /* Simply compare the altname list with the cached one
 173         * and if it does not fit 1:1, recreate the cached list
 174         * from scratch.
 175         */
 176        im = list_first_entry(&parent_im->altnames_list, typeof(*im),
 177                              altnames_list);
 178        rem = RTA_PAYLOAD(proplist);
 179        for (i = RTA_DATA(proplist); RTA_OK(i, rem);
 180             i = RTA_NEXT(i, rem)) {
 181                if (i->rta_type != IFLA_ALT_IFNAME)
 182                        continue;
 183                if (!im || strcmp(rta_getattr_str(i), im->name))
 184                        goto recreate_altname_entries;
 185                im = list_next_entry(im, altnames_list);
 186        }
 187        if (list_next_entry(im, altnames_list))
 188                goto recreate_altname_entries;
 189        return;
 190
 191recreate_altname_entries:
 192        ll_altname_entries_destroy(parent_im);
 193        ll_altname_entries_create(parent_im, ifi, tb);
 194}
 195
 196static void ll_entries_create(struct ifinfomsg *ifi, struct rtattr **tb)
 197{
 198        struct ll_cache *parent_im;
 199
 200        if (!tb[IFLA_IFNAME])
 201                return;
 202        parent_im = ll_entry_create(ifi, rta_getattr_str(tb[IFLA_IFNAME]),
 203                                    NULL);
 204        if (!parent_im)
 205                return;
 206        ll_altname_entries_create(parent_im, ifi, tb);
 207}
 208
 209static void ll_entries_destroy(struct ll_cache *parent_im)
 210{
 211        ll_altname_entries_destroy(parent_im);
 212        ll_entry_destroy(parent_im, true);
 213}
 214
 215static void ll_entries_update(struct ll_cache *parent_im,
 216                              struct ifinfomsg *ifi, struct rtattr **tb)
 217{
 218        if (tb[IFLA_IFNAME])
 219                ll_entry_update(parent_im, ifi,
 220                                rta_getattr_str(tb[IFLA_IFNAME]));
 221        ll_altname_entries_update(parent_im, ifi, tb);
 222}
 223
 224int ll_remember_index(struct nlmsghdr *n, void *arg)
 225{
 226        struct ifinfomsg *ifi = NLMSG_DATA(n);
 227        struct ll_cache *im;
 228        struct rtattr *tb[IFLA_MAX+1];
 229
 230        if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
 231                return 0;
 232
 233        if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
 234                return -1;
 235
 236        im = ll_get_by_index(ifi->ifi_index);
 237        if (n->nlmsg_type == RTM_DELLINK) {
 238                if (im)
 239                        ll_entries_destroy(im);
 240                return 0;
 241        }
 242
 243        parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi),
 244                           IFLA_PAYLOAD(n), NLA_F_NESTED);
 245        if (im)
 246                ll_entries_update(im, ifi, tb);
 247        else
 248                ll_entries_create(ifi, tb);
 249        return 0;
 250}
 251
 252const char *ll_idx_n2a(unsigned int idx)
 253{
 254        static char buf[IFNAMSIZ];
 255
 256        snprintf(buf, sizeof(buf), "if%u", idx);
 257        return buf;
 258}
 259
 260static unsigned int ll_idx_a2n(const char *name)
 261{
 262        unsigned int idx;
 263
 264        if (sscanf(name, "if%u", &idx) != 1)
 265                return 0;
 266        return idx;
 267}
 268
 269static int ll_link_get(const char *name, int index)
 270{
 271        struct {
 272                struct nlmsghdr         n;
 273                struct ifinfomsg        ifm;
 274                char                    buf[1024];
 275        } req = {
 276                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
 277                .n.nlmsg_flags = NLM_F_REQUEST,
 278                .n.nlmsg_type = RTM_GETLINK,
 279                .ifm.ifi_index = index,
 280        };
 281        __u32 filt_mask = RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS;
 282        struct rtnl_handle rth = {};
 283        struct nlmsghdr *answer;
 284        int rc = 0;
 285
 286        if (rtnl_open(&rth, 0) < 0)
 287                return 0;
 288
 289        addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask);
 290        if (name)
 291                addattr_l(&req.n, sizeof(req),
 292                          !check_ifname(name) ? IFLA_IFNAME : IFLA_ALT_IFNAME,
 293                          name, strlen(name) + 1);
 294
 295        if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0)
 296                goto out;
 297
 298        /* add entry to cache */
 299        rc  = ll_remember_index(answer, NULL);
 300        if (!rc) {
 301                struct ifinfomsg *ifm = NLMSG_DATA(answer);
 302
 303                rc = ifm->ifi_index;
 304        }
 305
 306        free(answer);
 307out:
 308        rtnl_close(&rth);
 309        return rc;
 310}
 311
 312const char *ll_index_to_name(unsigned int idx)
 313{
 314        static char buf[IFNAMSIZ];
 315        const struct ll_cache *im;
 316
 317        if (idx == 0)
 318                return "*";
 319
 320        im = ll_get_by_index(idx);
 321        if (im)
 322                return im->name;
 323
 324        if (ll_link_get(NULL, idx) == idx) {
 325                im = ll_get_by_index(idx);
 326                if (im)
 327                        return im->name;
 328        }
 329
 330        if (if_indextoname(idx, buf) == NULL)
 331                snprintf(buf, IFNAMSIZ, "if%u", idx);
 332
 333        return buf;
 334}
 335
 336int ll_index_to_type(unsigned idx)
 337{
 338        const struct ll_cache *im;
 339
 340        if (idx == 0)
 341                return -1;
 342
 343        im = ll_get_by_index(idx);
 344        return im ? im->type : -1;
 345}
 346
 347int ll_index_to_flags(unsigned idx)
 348{
 349        const struct ll_cache *im;
 350
 351        if (idx == 0)
 352                return 0;
 353
 354        im = ll_get_by_index(idx);
 355        return im ? im->flags : -1;
 356}
 357
 358unsigned ll_name_to_index(const char *name)
 359{
 360        const struct ll_cache *im;
 361        unsigned idx;
 362
 363        if (name == NULL)
 364                return 0;
 365
 366        im = ll_get_by_name(name);
 367        if (im)
 368                return im->index;
 369
 370        idx = ll_link_get(name, 0);
 371        if (idx == 0)
 372                idx = if_nametoindex(name);
 373        if (idx == 0)
 374                idx = ll_idx_a2n(name);
 375        return idx;
 376}
 377
 378void ll_drop_by_index(unsigned index)
 379{
 380        struct ll_cache *im;
 381
 382        im = ll_get_by_index(index);
 383        if (!im)
 384                return;
 385
 386        hlist_del(&im->idx_hash);
 387        hlist_del(&im->name_hash);
 388
 389        free(im);
 390}
 391
 392void ll_init_map(struct rtnl_handle *rth)
 393{
 394        static int initialized;
 395
 396        if (initialized)
 397                return;
 398
 399        if (rtnl_linkdump_req(rth, AF_UNSPEC) < 0) {
 400                perror("Cannot send dump request");
 401                exit(1);
 402        }
 403
 404        if (rtnl_dump_filter(rth, ll_remember_index, NULL) < 0) {
 405                fprintf(stderr, "Dump terminated\n");
 406                exit(1);
 407        }
 408
 409        initialized = 1;
 410}
 411