busybox/networking/libiproute/libnetlink.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * This program is free software; you can redistribute it and/or
   4 * modify it under the terms of the GNU General Public License
   5 * as published by the Free Software Foundation; either version
   6 * 2 of the License, or (at your option) any later version.
   7 *
   8 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   9 */
  10
  11#include <sys/socket.h>
  12#include <sys/uio.h>
  13
  14#include "libbb.h"
  15#include "libnetlink.h"
  16
  17void FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
  18{
  19        socklen_t addr_len;
  20
  21        memset(rth, 0, sizeof(*rth));
  22        rth->fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  23        rth->local.nl_family = AF_NETLINK;
  24        /*rth->local.nl_groups = subscriptions;*/
  25
  26        xbind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local));
  27        addr_len = sizeof(rth->local);
  28        getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len);
  29
  30/* too much paranoia
  31        if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0)
  32                bb_perror_msg_and_die("getsockname");
  33        if (addr_len != sizeof(rth->local))
  34                bb_error_msg_and_die("wrong address length %d", addr_len);
  35        if (rth->local.nl_family != AF_NETLINK)
  36                bb_error_msg_and_die("wrong address family %d", rth->local.nl_family);
  37*/
  38        rth->seq = time(NULL);
  39}
  40
  41int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
  42{
  43        struct {
  44                struct nlmsghdr nlh;
  45                struct rtgenmsg g;
  46        } req;
  47
  48        req.nlh.nlmsg_len = sizeof(req);
  49        req.nlh.nlmsg_type = type;
  50        req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
  51        req.nlh.nlmsg_pid = 0;
  52        req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
  53        req.g.rtgen_family = family;
  54
  55        return rtnl_send(rth, (void*)&req, sizeof(req));
  56}
  57
  58//TODO: pass rth->fd instead of full rth?
  59int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
  60{
  61        struct sockaddr_nl nladdr;
  62
  63        memset(&nladdr, 0, sizeof(nladdr));
  64        nladdr.nl_family = AF_NETLINK;
  65
  66        return xsendto(rth->fd, buf, len, (struct sockaddr*)&nladdr, sizeof(nladdr));
  67}
  68
  69int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
  70{
  71        struct nlmsghdr nlh;
  72        struct sockaddr_nl nladdr;
  73        struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
  74        /* Use designated initializers, struct layout is non-portable */
  75        struct msghdr msg = {
  76                .msg_name = (void*)&nladdr,
  77                .msg_namelen = sizeof(nladdr),
  78                .msg_iov = iov,
  79                .msg_iovlen = 2,
  80                .msg_control = NULL,
  81                .msg_controllen = 0,
  82                .msg_flags = 0
  83        };
  84
  85        memset(&nladdr, 0, sizeof(nladdr));
  86        nladdr.nl_family = AF_NETLINK;
  87
  88        nlh.nlmsg_len = NLMSG_LENGTH(len);
  89        nlh.nlmsg_type = type;
  90        nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
  91        nlh.nlmsg_pid = 0;
  92        nlh.nlmsg_seq = rth->dump = ++rth->seq;
  93
  94        return sendmsg(rth->fd, &msg, 0);
  95}
  96
  97static int rtnl_dump_filter(struct rtnl_handle *rth,
  98                int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *n, void *) FAST_FUNC,
  99                void *arg1/*,
 100                int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
 101                void *arg2*/)
 102{
 103        int retval = -1;
 104        char *buf = xmalloc(8*1024); /* avoid big stack buffer */
 105        struct sockaddr_nl nladdr;
 106        struct iovec iov = { buf, 8*1024 };
 107
 108        while (1) {
 109                int status;
 110                struct nlmsghdr *h;
 111                /* Use designated initializers, struct layout is non-portable */
 112                struct msghdr msg = {
 113                        .msg_name = (void*)&nladdr,
 114                        .msg_namelen = sizeof(nladdr),
 115                        .msg_iov = &iov,
 116                        .msg_iovlen = 1,
 117                        .msg_control = NULL,
 118                        .msg_controllen = 0,
 119                        .msg_flags = 0
 120                };
 121
 122                status = recvmsg(rth->fd, &msg, 0);
 123
 124                if (status < 0) {
 125                        if (errno == EINTR)
 126                                continue;
 127                        bb_perror_msg("OVERRUN");
 128                        continue;
 129                }
 130                if (status == 0) {
 131                        bb_error_msg("EOF on netlink");
 132                        goto ret;
 133                }
 134                if (msg.msg_namelen != sizeof(nladdr)) {
 135                        bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
 136                }
 137
 138                h = (struct nlmsghdr*)buf;
 139                while (NLMSG_OK(h, status)) {
 140                        int err;
 141
 142                        if (nladdr.nl_pid != 0 ||
 143                            h->nlmsg_pid != rth->local.nl_pid ||
 144                            h->nlmsg_seq != rth->dump
 145                        ) {
 146//                              if (junk) {
 147//                                      err = junk(&nladdr, h, arg2);
 148//                                      if (err < 0) {
 149//                                              retval = err;
 150//                                              goto ret;
 151//                                      }
 152//                              }
 153                                goto skip_it;
 154                        }
 155
 156                        if (h->nlmsg_type == NLMSG_DONE) {
 157                                goto ret_0;
 158                        }
 159                        if (h->nlmsg_type == NLMSG_ERROR) {
 160                                struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
 161                                if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
 162                                        bb_error_msg("ERROR truncated");
 163                                } else {
 164                                        errno = -l_err->error;
 165                                        bb_perror_msg("RTNETLINK answers");
 166                                }
 167                                goto ret;
 168                        }
 169                        err = filter(&nladdr, h, arg1);
 170                        if (err < 0) {
 171                                retval = err;
 172                                goto ret;
 173                        }
 174
 175 skip_it:
 176                        h = NLMSG_NEXT(h, status);
 177                }
 178                if (msg.msg_flags & MSG_TRUNC) {
 179                        bb_error_msg("message truncated");
 180                        continue;
 181                }
 182                if (status) {
 183                        bb_error_msg_and_die("remnant of size %d!", status);
 184                }
 185        } /* while (1) */
 186 ret_0:
 187        retval++; /* = 0 */
 188 ret:
 189        free(buf);
 190        return retval;
 191}
 192
 193int FAST_FUNC xrtnl_dump_filter(struct rtnl_handle *rth,
 194                int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *, void *) FAST_FUNC,
 195                void *arg1)
 196{
 197        int ret = rtnl_dump_filter(rth, filter, arg1/*, NULL, NULL*/);
 198        if (ret < 0)
 199                bb_error_msg_and_die("dump terminated");
 200        return ret;
 201}
 202
 203int FAST_FUNC rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
 204                pid_t peer, unsigned groups,
 205                struct nlmsghdr *answer,
 206                int (*junk)(struct sockaddr_nl *, struct nlmsghdr *, void *),
 207                void *jarg)
 208{
 209/* bbox doesn't use parameters no. 3, 4, 6, 7, they are stubbed out */
 210#define peer   0
 211#define groups 0
 212#define junk   NULL
 213#define jarg   NULL
 214        int retval = -1;
 215        int status;
 216        unsigned seq;
 217        struct nlmsghdr *h;
 218        struct sockaddr_nl nladdr;
 219        struct iovec iov = { (void*)n, n->nlmsg_len };
 220        char   *buf = xmalloc(8*1024); /* avoid big stack buffer */
 221        /* Use designated initializers, struct layout is non-portable */
 222        struct msghdr msg = {
 223                .msg_name = (void*)&nladdr,
 224                .msg_namelen = sizeof(nladdr),
 225                .msg_iov = &iov,
 226                .msg_iovlen = 1,
 227                .msg_control = NULL,
 228                .msg_controllen = 0,
 229                .msg_flags = 0
 230        };
 231
 232        memset(&nladdr, 0, sizeof(nladdr));
 233        nladdr.nl_family = AF_NETLINK;
 234//      nladdr.nl_pid = peer;
 235//      nladdr.nl_groups = groups;
 236
 237        n->nlmsg_seq = seq = ++rtnl->seq;
 238        if (answer == NULL) {
 239                n->nlmsg_flags |= NLM_F_ACK;
 240        }
 241        status = sendmsg(rtnl->fd, &msg, 0);
 242
 243        if (status < 0) {
 244                bb_perror_msg("can't talk to rtnetlink");
 245                goto ret;
 246        }
 247
 248        iov.iov_base = buf;
 249
 250        while (1) {
 251                iov.iov_len = 8*1024;
 252                status = recvmsg(rtnl->fd, &msg, 0);
 253
 254                if (status < 0) {
 255                        if (errno == EINTR) {
 256                                continue;
 257                        }
 258                        bb_perror_msg("OVERRUN");
 259                        continue;
 260                }
 261                if (status == 0) {
 262                        bb_error_msg("EOF on netlink");
 263                        goto ret;
 264                }
 265                if (msg.msg_namelen != sizeof(nladdr)) {
 266                        bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
 267                }
 268                for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
 269//                      int l_err;
 270                        int len = h->nlmsg_len;
 271                        int l = len - sizeof(*h);
 272
 273                        if (l < 0 || len > status) {
 274                                if (msg.msg_flags & MSG_TRUNC) {
 275                                        bb_error_msg("truncated message");
 276                                        goto ret;
 277                                }
 278                                bb_error_msg_and_die("malformed message: len=%d!", len);
 279                        }
 280
 281                        if (nladdr.nl_pid != peer ||
 282                            h->nlmsg_pid != rtnl->local.nl_pid ||
 283                            h->nlmsg_seq != seq
 284                        ) {
 285//                              if (junk) {
 286//                                      l_err = junk(&nladdr, h, jarg);
 287//                                      if (l_err < 0) {
 288//                                              retval = l_err;
 289//                                              goto ret;
 290//                                      }
 291//                              }
 292                                continue;
 293                        }
 294
 295                        if (h->nlmsg_type == NLMSG_ERROR) {
 296                                struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
 297                                if (l < (int)sizeof(struct nlmsgerr)) {
 298                                        bb_error_msg("ERROR truncated");
 299                                } else {
 300                                        errno = - err->error;
 301                                        if (errno == 0) {
 302                                                if (answer) {
 303                                                        memcpy(answer, h, h->nlmsg_len);
 304                                                }
 305                                                goto ret_0;
 306                                        }
 307                                        bb_perror_msg("RTNETLINK answers");
 308                                }
 309                                goto ret;
 310                        }
 311                        if (answer) {
 312                                memcpy(answer, h, h->nlmsg_len);
 313                                goto ret_0;
 314                        }
 315
 316                        bb_error_msg("unexpected reply!");
 317
 318                        status -= NLMSG_ALIGN(len);
 319                        h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
 320                }
 321                if (msg.msg_flags & MSG_TRUNC) {
 322                        bb_error_msg("message truncated");
 323                        continue;
 324                }
 325                if (status) {
 326                        bb_error_msg_and_die("remnant of size %d!", status);
 327                }
 328        } /* while (1) */
 329 ret_0:
 330        retval++; /* = 0 */
 331 ret:
 332        free(buf);
 333        return retval;
 334}
 335
 336int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
 337{
 338        int len = RTA_LENGTH(4);
 339        struct rtattr *rta;
 340
 341        if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
 342                return -1;
 343        }
 344        rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
 345        rta->rta_type = type;
 346        rta->rta_len = len;
 347        move_to_unaligned32(RTA_DATA(rta), data);
 348        n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
 349        return 0;
 350}
 351
 352int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
 353{
 354        int len = RTA_LENGTH(alen);
 355        struct rtattr *rta;
 356
 357        if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
 358                return -1;
 359        }
 360        rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
 361        rta->rta_type = type;
 362        rta->rta_len = len;
 363        memcpy(RTA_DATA(rta), data, alen);
 364        n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
 365        return 0;
 366}
 367
 368int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data)
 369{
 370        int len = RTA_LENGTH(4);
 371        struct rtattr *subrta;
 372
 373        if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
 374                return -1;
 375        }
 376        subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
 377        subrta->rta_type = type;
 378        subrta->rta_len = len;
 379        move_to_unaligned32(RTA_DATA(subrta), data);
 380        rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
 381        return 0;
 382}
 383
 384int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
 385{
 386        struct rtattr *subrta;
 387        int len = RTA_LENGTH(alen);
 388
 389        if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
 390                return -1;
 391        }
 392        subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
 393        subrta->rta_type = type;
 394        subrta->rta_len = len;
 395        memcpy(RTA_DATA(subrta), data, alen);
 396        rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
 397        return 0;
 398}
 399
 400
 401void FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
 402{
 403        while (RTA_OK(rta, len)) {
 404                if (rta->rta_type <= max) {
 405                        tb[rta->rta_type] = rta;
 406                }
 407                rta = RTA_NEXT(rta, len);
 408        }
 409        if (len) {
 410                bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
 411        }
 412}
 413