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