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