rtnl.c 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
  4. */
  5. #define _GNU_SOURCE
  6. #include <sys/types.h>
  7. #include <sys/socket.h>
  8. #include <netlink/msg.h>
  9. #include <netlink/attr.h>
  10. #include <netlink/socket.h>
  11. #include <linux/rtnetlink.h>
  12. #include "unetd.h"
  13. static struct nl_sock *rtnl;
  14. bool rtnl_ignore_errors;
  15. static int
  16. unetd_nl_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err,
  17. void *arg)
  18. {
  19. struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
  20. struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
  21. struct nlattr *attrs;
  22. int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
  23. int len = nlh->nlmsg_len;
  24. const char *errstr = "(unknown)";
  25. if (rtnl_ignore_errors)
  26. return NL_STOP;
  27. if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
  28. return NL_STOP;
  29. if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
  30. ack_len += err->msg.nlmsg_len - sizeof(*nlh);
  31. attrs = (void *) ((unsigned char *) nlh + ack_len);
  32. len -= ack_len;
  33. nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
  34. if (tb[NLMSGERR_ATTR_MSG])
  35. errstr = nla_data(tb[NLMSGERR_ATTR_MSG]);
  36. D("Netlink error(%d): %s\n", err->error, errstr);
  37. return NL_STOP;
  38. }
  39. int rtnl_call(struct nl_msg *msg)
  40. {
  41. int ret;
  42. ret = nl_send_auto_complete(rtnl, msg);
  43. nlmsg_free(msg);
  44. if (ret < 0)
  45. return ret;
  46. return nl_wait_for_ack(rtnl);
  47. }
  48. int rtnl_init(void)
  49. {
  50. int fd, opt;
  51. if (rtnl)
  52. return 0;
  53. rtnl = nl_socket_alloc();
  54. if (!rtnl)
  55. return -1;
  56. if (nl_connect(rtnl, NETLINK_ROUTE))
  57. goto free;
  58. nl_socket_disable_seq_check(rtnl);
  59. nl_socket_set_buffer_size(rtnl, 65536, 0);
  60. nl_cb_err(nl_socket_get_cb(rtnl), NL_CB_CUSTOM, unetd_nl_error_cb, NULL);
  61. fd = nl_socket_get_fd(rtnl);
  62. opt = 1;
  63. setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt));
  64. opt = 1;
  65. setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &opt, sizeof(opt));
  66. return 0;
  67. free:
  68. nl_socket_free(rtnl);
  69. rtnl = NULL;
  70. return -1;
  71. }