.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * NETLINK Kernel-user communication protocol. |
---|
3 | 4 | * |
---|
4 | 5 | * Authors: Alan Cox <alan@lxorguk.ukuu.org.uk> |
---|
5 | 6 | * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
---|
6 | 7 | * Patrick McHardy <kaber@trash.net> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License |
---|
10 | | - * as published by the Free Software Foundation; either version |
---|
11 | | - * 2 of the License, or (at your option) any later version. |
---|
12 | 8 | * |
---|
13 | 9 | * Tue Jun 26 14:36:48 MEST 2001 Herbert "herp" Rosmanith |
---|
14 | 10 | * added netlink_proto_exit |
---|
.. | .. |
---|
64 | 60 | #include <linux/genetlink.h> |
---|
65 | 61 | #include <linux/net_namespace.h> |
---|
66 | 62 | #include <linux/nospec.h> |
---|
| 63 | +#include <linux/btf_ids.h> |
---|
67 | 64 | |
---|
68 | 65 | #include <net/net_namespace.h> |
---|
69 | 66 | #include <net/netns/generic.h> |
---|
.. | .. |
---|
75 | 72 | |
---|
76 | 73 | struct listeners { |
---|
77 | 74 | struct rcu_head rcu; |
---|
78 | | - unsigned long masks[0]; |
---|
| 75 | + unsigned long masks[]; |
---|
79 | 76 | }; |
---|
80 | 77 | |
---|
81 | 78 | /* state bits */ |
---|
.. | .. |
---|
152 | 149 | |
---|
153 | 150 | static inline u32 netlink_group_mask(u32 group) |
---|
154 | 151 | { |
---|
| 152 | + if (group > 32) |
---|
| 153 | + return 0; |
---|
155 | 154 | return group ? 1 << (group - 1) : 0; |
---|
156 | 155 | } |
---|
157 | 156 | |
---|
.. | .. |
---|
245 | 244 | return 0; |
---|
246 | 245 | } |
---|
247 | 246 | |
---|
248 | | -static void __net_exit netlink_tap_exit_net(struct net *net) |
---|
249 | | -{ |
---|
250 | | -} |
---|
251 | | - |
---|
252 | 247 | static struct pernet_operations netlink_tap_net_ops = { |
---|
253 | 248 | .init = netlink_tap_init_net, |
---|
254 | | - .exit = netlink_tap_exit_net, |
---|
255 | 249 | .id = &netlink_tap_net_id, |
---|
256 | 250 | .size = sizeof(struct netlink_tap_net), |
---|
257 | 251 | }; |
---|
.. | .. |
---|
361 | 355 | { |
---|
362 | 356 | struct netlink_sock *nlk = nlk_sk(sk); |
---|
363 | 357 | |
---|
364 | | - if (skb_queue_empty(&sk->sk_receive_queue)) |
---|
| 358 | + if (skb_queue_empty_lockless(&sk->sk_receive_queue)) |
---|
365 | 359 | clear_bit(NETLINK_S_CONGESTED, &nlk->state); |
---|
366 | 360 | if (!test_bit(NETLINK_S_CONGESTED, &nlk->state)) |
---|
367 | 361 | wake_up_interruptible(&nlk->wait); |
---|
.. | .. |
---|
576 | 570 | if (nlk_sk(sk)->bound) |
---|
577 | 571 | goto err; |
---|
578 | 572 | |
---|
579 | | - err = -ENOMEM; |
---|
580 | | - if (BITS_PER_LONG > 32 && |
---|
581 | | - unlikely(atomic_read(&table->hash.nelems) >= UINT_MAX)) |
---|
582 | | - goto err; |
---|
| 573 | + /* portid can be read locklessly from netlink_getname(). */ |
---|
| 574 | + WRITE_ONCE(nlk_sk(sk)->portid, portid); |
---|
583 | 575 | |
---|
584 | | - nlk_sk(sk)->portid = portid; |
---|
585 | 576 | sock_hold(sk); |
---|
586 | 577 | |
---|
587 | 578 | err = __netlink_insert(table, sk); |
---|
.. | .. |
---|
866 | 857 | * |
---|
867 | 858 | * Test to see if the opener of the socket we received the message |
---|
868 | 859 | * from had when the netlink socket was created and the sender of the |
---|
869 | | - * message has has the capability @cap in the user namespace @user_ns. |
---|
| 860 | + * message has the capability @cap in the user namespace @user_ns. |
---|
870 | 861 | */ |
---|
871 | 862 | bool __netlink_ns_capable(const struct netlink_skb_parms *nsp, |
---|
872 | 863 | struct user_namespace *user_ns, int cap) |
---|
.. | .. |
---|
885 | 876 | * |
---|
886 | 877 | * Test to see if the opener of the socket we received the message |
---|
887 | 878 | * from had when the netlink socket was created and the sender of the |
---|
888 | | - * message has has the capability @cap in the user namespace @user_ns. |
---|
| 879 | + * message has the capability @cap in the user namespace @user_ns. |
---|
889 | 880 | */ |
---|
890 | 881 | bool netlink_ns_capable(const struct sk_buff *skb, |
---|
891 | 882 | struct user_namespace *user_ns, int cap) |
---|
.. | .. |
---|
901 | 892 | * |
---|
902 | 893 | * Test to see if the opener of the socket we received the message |
---|
903 | 894 | * from had when the netlink socket was created and the sender of the |
---|
904 | | - * message has has the capability @cap in all user namespaces. |
---|
| 895 | + * message has the capability @cap in all user namespaces. |
---|
905 | 896 | */ |
---|
906 | 897 | bool netlink_capable(const struct sk_buff *skb, int cap) |
---|
907 | 898 | { |
---|
.. | .. |
---|
916 | 907 | * |
---|
917 | 908 | * Test to see if the opener of the socket we received the message |
---|
918 | 909 | * from had when the netlink socket was created and the sender of the |
---|
919 | | - * message has has the capability @cap over the network namespace of |
---|
| 910 | + * message has the capability @cap over the network namespace of |
---|
920 | 911 | * the socket we received the message from. |
---|
921 | 912 | */ |
---|
922 | 913 | bool netlink_net_capable(const struct sk_buff *skb, int cap) |
---|
.. | .. |
---|
998 | 989 | struct netlink_sock *nlk = nlk_sk(sk); |
---|
999 | 990 | struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; |
---|
1000 | 991 | int err = 0; |
---|
1001 | | - long unsigned int groups = nladdr->nl_groups; |
---|
| 992 | + unsigned long groups; |
---|
1002 | 993 | bool bound; |
---|
1003 | 994 | |
---|
1004 | 995 | if (addr_len < sizeof(struct sockaddr_nl)) |
---|
.. | .. |
---|
1006 | 997 | |
---|
1007 | 998 | if (nladdr->nl_family != AF_NETLINK) |
---|
1008 | 999 | return -EINVAL; |
---|
| 1000 | + groups = nladdr->nl_groups; |
---|
1009 | 1001 | |
---|
1010 | 1002 | /* Only superuser is allowed to listen multicasts */ |
---|
1011 | 1003 | if (groups) { |
---|
.. | .. |
---|
1016 | 1008 | return err; |
---|
1017 | 1009 | } |
---|
1018 | 1010 | |
---|
1019 | | - if (nlk->ngroups == 0) |
---|
1020 | | - groups = 0; |
---|
1021 | | - else if (nlk->ngroups < 8*sizeof(groups)) |
---|
| 1011 | + if (nlk->ngroups < BITS_PER_LONG) |
---|
1022 | 1012 | groups &= (1UL << nlk->ngroups) - 1; |
---|
1023 | 1013 | |
---|
1024 | 1014 | /* Paired with WRITE_ONCE() in netlink_insert() */ |
---|
.. | .. |
---|
1091 | 1081 | return -EINVAL; |
---|
1092 | 1082 | |
---|
1093 | 1083 | if (addr->sa_family == AF_UNSPEC) { |
---|
1094 | | - sk->sk_state = NETLINK_UNCONNECTED; |
---|
1095 | | - nlk->dst_portid = 0; |
---|
1096 | | - nlk->dst_group = 0; |
---|
| 1084 | + /* paired with READ_ONCE() in netlink_getsockbyportid() */ |
---|
| 1085 | + WRITE_ONCE(sk->sk_state, NETLINK_UNCONNECTED); |
---|
| 1086 | + /* dst_portid and dst_group can be read locklessly */ |
---|
| 1087 | + WRITE_ONCE(nlk->dst_portid, 0); |
---|
| 1088 | + WRITE_ONCE(nlk->dst_group, 0); |
---|
1097 | 1089 | return 0; |
---|
1098 | 1090 | } |
---|
1099 | 1091 | if (addr->sa_family != AF_NETLINK) |
---|
.. | .. |
---|
1114 | 1106 | err = netlink_autobind(sock); |
---|
1115 | 1107 | |
---|
1116 | 1108 | if (err == 0) { |
---|
1117 | | - sk->sk_state = NETLINK_CONNECTED; |
---|
1118 | | - nlk->dst_portid = nladdr->nl_pid; |
---|
1119 | | - nlk->dst_group = ffs(nladdr->nl_groups); |
---|
| 1109 | + /* paired with READ_ONCE() in netlink_getsockbyportid() */ |
---|
| 1110 | + WRITE_ONCE(sk->sk_state, NETLINK_CONNECTED); |
---|
| 1111 | + /* dst_portid and dst_group can be read locklessly */ |
---|
| 1112 | + WRITE_ONCE(nlk->dst_portid, nladdr->nl_pid); |
---|
| 1113 | + WRITE_ONCE(nlk->dst_group, ffs(nladdr->nl_groups)); |
---|
1120 | 1114 | } |
---|
1121 | 1115 | |
---|
1122 | 1116 | return err; |
---|
.. | .. |
---|
1133 | 1127 | nladdr->nl_pad = 0; |
---|
1134 | 1128 | |
---|
1135 | 1129 | if (peer) { |
---|
1136 | | - nladdr->nl_pid = nlk->dst_portid; |
---|
1137 | | - nladdr->nl_groups = netlink_group_mask(nlk->dst_group); |
---|
| 1130 | + /* Paired with WRITE_ONCE() in netlink_connect() */ |
---|
| 1131 | + nladdr->nl_pid = READ_ONCE(nlk->dst_portid); |
---|
| 1132 | + nladdr->nl_groups = netlink_group_mask(READ_ONCE(nlk->dst_group)); |
---|
1138 | 1133 | } else { |
---|
1139 | | - nladdr->nl_pid = nlk->portid; |
---|
| 1134 | + /* Paired with WRITE_ONCE() in netlink_insert() */ |
---|
| 1135 | + nladdr->nl_pid = READ_ONCE(nlk->portid); |
---|
1140 | 1136 | netlink_lock_table(); |
---|
1141 | 1137 | nladdr->nl_groups = nlk->groups ? nlk->groups[0] : 0; |
---|
1142 | 1138 | netlink_unlock_table(); |
---|
.. | .. |
---|
1163 | 1159 | |
---|
1164 | 1160 | /* Don't bother queuing skb if kernel socket has no input function */ |
---|
1165 | 1161 | nlk = nlk_sk(sock); |
---|
1166 | | - if (sock->sk_state == NETLINK_CONNECTED && |
---|
1167 | | - nlk->dst_portid != nlk_sk(ssk)->portid) { |
---|
| 1162 | + /* dst_portid and sk_state can be changed in netlink_connect() */ |
---|
| 1163 | + if (READ_ONCE(sock->sk_state) == NETLINK_CONNECTED && |
---|
| 1164 | + READ_ONCE(nlk->dst_portid) != nlk_sk(ssk)->portid) { |
---|
1168 | 1165 | sock_put(sock); |
---|
1169 | 1166 | return ERR_PTR(-ECONNREFUSED); |
---|
1170 | 1167 | } |
---|
.. | .. |
---|
1386 | 1383 | } |
---|
1387 | 1384 | EXPORT_SYMBOL_GPL(netlink_has_listeners); |
---|
1388 | 1385 | |
---|
| 1386 | +bool netlink_strict_get_check(struct sk_buff *skb) |
---|
| 1387 | +{ |
---|
| 1388 | + const struct netlink_sock *nlk = nlk_sk(NETLINK_CB(skb).sk); |
---|
| 1389 | + |
---|
| 1390 | + return nlk->flags & NETLINK_F_STRICT_CHK; |
---|
| 1391 | +} |
---|
| 1392 | +EXPORT_SYMBOL_GPL(netlink_strict_get_check); |
---|
| 1393 | + |
---|
1389 | 1394 | static int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) |
---|
1390 | 1395 | { |
---|
1391 | 1396 | struct netlink_sock *nlk = nlk_sk(sk); |
---|
.. | .. |
---|
1597 | 1602 | int netlink_set_err(struct sock *ssk, u32 portid, u32 group, int code) |
---|
1598 | 1603 | { |
---|
1599 | 1604 | struct netlink_set_err_data info; |
---|
| 1605 | + unsigned long flags; |
---|
1600 | 1606 | struct sock *sk; |
---|
1601 | 1607 | int ret = 0; |
---|
1602 | 1608 | |
---|
.. | .. |
---|
1606 | 1612 | /* sk->sk_err wants a positive error value */ |
---|
1607 | 1613 | info.code = -code; |
---|
1608 | 1614 | |
---|
1609 | | - read_lock(&nl_table_lock); |
---|
| 1615 | + read_lock_irqsave(&nl_table_lock, flags); |
---|
1610 | 1616 | |
---|
1611 | 1617 | sk_for_each_bound(sk, &nl_table[ssk->sk_protocol].mc_list) |
---|
1612 | 1618 | ret += do_one_set_err(sk, &info); |
---|
1613 | 1619 | |
---|
1614 | | - read_unlock(&nl_table_lock); |
---|
| 1620 | + read_unlock_irqrestore(&nl_table_lock, flags); |
---|
1615 | 1621 | return ret; |
---|
1616 | 1622 | } |
---|
1617 | 1623 | EXPORT_SYMBOL(netlink_set_err); |
---|
.. | .. |
---|
1634 | 1640 | } |
---|
1635 | 1641 | |
---|
1636 | 1642 | static int netlink_setsockopt(struct socket *sock, int level, int optname, |
---|
1637 | | - char __user *optval, unsigned int optlen) |
---|
| 1643 | + sockptr_t optval, unsigned int optlen) |
---|
1638 | 1644 | { |
---|
1639 | 1645 | struct sock *sk = sock->sk; |
---|
1640 | 1646 | struct netlink_sock *nlk = nlk_sk(sk); |
---|
.. | .. |
---|
1645 | 1651 | return -ENOPROTOOPT; |
---|
1646 | 1652 | |
---|
1647 | 1653 | if (optlen >= sizeof(int) && |
---|
1648 | | - get_user(val, (unsigned int __user *)optval)) |
---|
| 1654 | + copy_from_sockptr(&val, optval, sizeof(val))) |
---|
1649 | 1655 | return -EFAULT; |
---|
1650 | 1656 | |
---|
1651 | 1657 | switch (optname) { |
---|
.. | .. |
---|
1721 | 1727 | nlk->flags &= ~NETLINK_F_EXT_ACK; |
---|
1722 | 1728 | err = 0; |
---|
1723 | 1729 | break; |
---|
| 1730 | + case NETLINK_GET_STRICT_CHK: |
---|
| 1731 | + if (val) |
---|
| 1732 | + nlk->flags |= NETLINK_F_STRICT_CHK; |
---|
| 1733 | + else |
---|
| 1734 | + nlk->flags &= ~NETLINK_F_STRICT_CHK; |
---|
| 1735 | + err = 0; |
---|
| 1736 | + break; |
---|
1724 | 1737 | default: |
---|
1725 | 1738 | err = -ENOPROTOOPT; |
---|
1726 | 1739 | } |
---|
.. | .. |
---|
1732 | 1745 | { |
---|
1733 | 1746 | struct sock *sk = sock->sk; |
---|
1734 | 1747 | struct netlink_sock *nlk = nlk_sk(sk); |
---|
1735 | | - int len, val, err; |
---|
| 1748 | + unsigned int flag; |
---|
| 1749 | + int len, val; |
---|
1736 | 1750 | |
---|
1737 | 1751 | if (level != SOL_NETLINK) |
---|
1738 | 1752 | return -ENOPROTOOPT; |
---|
.. | .. |
---|
1744 | 1758 | |
---|
1745 | 1759 | switch (optname) { |
---|
1746 | 1760 | case NETLINK_PKTINFO: |
---|
1747 | | - if (len < sizeof(int)) |
---|
1748 | | - return -EINVAL; |
---|
1749 | | - len = sizeof(int); |
---|
1750 | | - val = nlk->flags & NETLINK_F_RECV_PKTINFO ? 1 : 0; |
---|
1751 | | - if (put_user(len, optlen) || |
---|
1752 | | - put_user(val, optval)) |
---|
1753 | | - return -EFAULT; |
---|
1754 | | - err = 0; |
---|
| 1761 | + flag = NETLINK_F_RECV_PKTINFO; |
---|
1755 | 1762 | break; |
---|
1756 | 1763 | case NETLINK_BROADCAST_ERROR: |
---|
1757 | | - if (len < sizeof(int)) |
---|
1758 | | - return -EINVAL; |
---|
1759 | | - len = sizeof(int); |
---|
1760 | | - val = nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR ? 1 : 0; |
---|
1761 | | - if (put_user(len, optlen) || |
---|
1762 | | - put_user(val, optval)) |
---|
1763 | | - return -EFAULT; |
---|
1764 | | - err = 0; |
---|
| 1764 | + flag = NETLINK_F_BROADCAST_SEND_ERROR; |
---|
1765 | 1765 | break; |
---|
1766 | 1766 | case NETLINK_NO_ENOBUFS: |
---|
1767 | | - if (len < sizeof(int)) |
---|
1768 | | - return -EINVAL; |
---|
1769 | | - len = sizeof(int); |
---|
1770 | | - val = nlk->flags & NETLINK_F_RECV_NO_ENOBUFS ? 1 : 0; |
---|
1771 | | - if (put_user(len, optlen) || |
---|
1772 | | - put_user(val, optval)) |
---|
1773 | | - return -EFAULT; |
---|
1774 | | - err = 0; |
---|
| 1767 | + flag = NETLINK_F_RECV_NO_ENOBUFS; |
---|
1775 | 1768 | break; |
---|
1776 | 1769 | case NETLINK_LIST_MEMBERSHIPS: { |
---|
1777 | | - int pos, idx, shift; |
---|
| 1770 | + int pos, idx, shift, err = 0; |
---|
1778 | 1771 | |
---|
1779 | | - err = 0; |
---|
1780 | 1772 | netlink_lock_table(); |
---|
1781 | 1773 | for (pos = 0; pos * 8 < nlk->ngroups; pos += sizeof(u32)) { |
---|
1782 | 1774 | if (len - pos < sizeof(u32)) |
---|
.. | .. |
---|
1790 | 1782 | break; |
---|
1791 | 1783 | } |
---|
1792 | 1784 | } |
---|
1793 | | - if (put_user(ALIGN(nlk->ngroups / 8, sizeof(u32)), optlen)) |
---|
| 1785 | + if (put_user(ALIGN(BITS_TO_BYTES(nlk->ngroups), sizeof(u32)), optlen)) |
---|
1794 | 1786 | err = -EFAULT; |
---|
1795 | 1787 | netlink_unlock_table(); |
---|
1796 | | - break; |
---|
| 1788 | + return err; |
---|
1797 | 1789 | } |
---|
1798 | 1790 | case NETLINK_CAP_ACK: |
---|
1799 | | - if (len < sizeof(int)) |
---|
1800 | | - return -EINVAL; |
---|
1801 | | - len = sizeof(int); |
---|
1802 | | - val = nlk->flags & NETLINK_F_CAP_ACK ? 1 : 0; |
---|
1803 | | - if (put_user(len, optlen) || |
---|
1804 | | - put_user(val, optval)) |
---|
1805 | | - return -EFAULT; |
---|
1806 | | - err = 0; |
---|
| 1791 | + flag = NETLINK_F_CAP_ACK; |
---|
1807 | 1792 | break; |
---|
1808 | 1793 | case NETLINK_EXT_ACK: |
---|
1809 | | - if (len < sizeof(int)) |
---|
1810 | | - return -EINVAL; |
---|
1811 | | - len = sizeof(int); |
---|
1812 | | - val = nlk->flags & NETLINK_F_EXT_ACK ? 1 : 0; |
---|
1813 | | - if (put_user(len, optlen) || put_user(val, optval)) |
---|
1814 | | - return -EFAULT; |
---|
1815 | | - err = 0; |
---|
| 1794 | + flag = NETLINK_F_EXT_ACK; |
---|
| 1795 | + break; |
---|
| 1796 | + case NETLINK_GET_STRICT_CHK: |
---|
| 1797 | + flag = NETLINK_F_STRICT_CHK; |
---|
1816 | 1798 | break; |
---|
1817 | 1799 | default: |
---|
1818 | | - err = -ENOPROTOOPT; |
---|
| 1800 | + return -ENOPROTOOPT; |
---|
1819 | 1801 | } |
---|
1820 | | - return err; |
---|
| 1802 | + |
---|
| 1803 | + if (len < sizeof(int)) |
---|
| 1804 | + return -EINVAL; |
---|
| 1805 | + |
---|
| 1806 | + len = sizeof(int); |
---|
| 1807 | + val = nlk->flags & flag ? 1 : 0; |
---|
| 1808 | + |
---|
| 1809 | + if (put_user(len, optlen) || |
---|
| 1810 | + copy_to_user(optval, &val, len)) |
---|
| 1811 | + return -EFAULT; |
---|
| 1812 | + |
---|
| 1813 | + return 0; |
---|
1821 | 1814 | } |
---|
1822 | 1815 | |
---|
1823 | 1816 | static void netlink_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) |
---|
.. | .. |
---|
1850 | 1843 | struct scm_cookie scm; |
---|
1851 | 1844 | u32 netlink_skb_flags = 0; |
---|
1852 | 1845 | |
---|
1853 | | - if (msg->msg_flags&MSG_OOB) |
---|
| 1846 | + if (msg->msg_flags & MSG_OOB) |
---|
1854 | 1847 | return -EOPNOTSUPP; |
---|
1855 | 1848 | |
---|
1856 | 1849 | if (len == 0) { |
---|
.. | .. |
---|
1876 | 1869 | goto out; |
---|
1877 | 1870 | netlink_skb_flags |= NETLINK_SKB_DST; |
---|
1878 | 1871 | } else { |
---|
1879 | | - dst_portid = nlk->dst_portid; |
---|
1880 | | - dst_group = nlk->dst_group; |
---|
| 1872 | + /* Paired with WRITE_ONCE() in netlink_connect() */ |
---|
| 1873 | + dst_portid = READ_ONCE(nlk->dst_portid); |
---|
| 1874 | + dst_group = READ_ONCE(nlk->dst_group); |
---|
1881 | 1875 | } |
---|
1882 | 1876 | |
---|
1883 | 1877 | /* Paired with WRITE_ONCE() in netlink_insert() */ |
---|
.. | .. |
---|
1919 | 1913 | refcount_inc(&skb->users); |
---|
1920 | 1914 | netlink_broadcast(sk, skb, dst_portid, dst_group, GFP_KERNEL); |
---|
1921 | 1915 | } |
---|
1922 | | - err = netlink_unicast(sk, skb, dst_portid, msg->msg_flags&MSG_DONTWAIT); |
---|
| 1916 | + err = netlink_unicast(sk, skb, dst_portid, msg->msg_flags & MSG_DONTWAIT); |
---|
1923 | 1917 | |
---|
1924 | 1918 | out: |
---|
1925 | 1919 | scm_destroy(&scm); |
---|
.. | .. |
---|
1932 | 1926 | struct scm_cookie scm; |
---|
1933 | 1927 | struct sock *sk = sock->sk; |
---|
1934 | 1928 | struct netlink_sock *nlk = nlk_sk(sk); |
---|
1935 | | - int noblock = flags&MSG_DONTWAIT; |
---|
| 1929 | + int noblock = flags & MSG_DONTWAIT; |
---|
1936 | 1930 | size_t copied; |
---|
1937 | 1931 | struct sk_buff *skb, *data_skb; |
---|
1938 | 1932 | int err, ret; |
---|
1939 | 1933 | |
---|
1940 | | - if (flags&MSG_OOB) |
---|
| 1934 | + if (flags & MSG_OOB) |
---|
1941 | 1935 | return -EOPNOTSUPP; |
---|
1942 | 1936 | |
---|
1943 | 1937 | copied = 0; |
---|
.. | .. |
---|
1976 | 1970 | copied = len; |
---|
1977 | 1971 | } |
---|
1978 | 1972 | |
---|
1979 | | - skb_reset_transport_header(data_skb); |
---|
1980 | 1973 | err = skb_copy_datagram_msg(data_skb, 0, msg, copied); |
---|
1981 | 1974 | |
---|
1982 | 1975 | if (msg->msg_name) { |
---|
.. | .. |
---|
2000 | 1993 | |
---|
2001 | 1994 | skb_free_datagram(sk, skb); |
---|
2002 | 1995 | |
---|
2003 | | - if (nlk->cb_running && |
---|
| 1996 | + if (READ_ONCE(nlk->cb_running) && |
---|
2004 | 1997 | atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { |
---|
2005 | 1998 | ret = netlink_dump(sk); |
---|
2006 | 1999 | if (ret) { |
---|
.. | .. |
---|
2189 | 2182 | * It would be better to create kernel thread. |
---|
2190 | 2183 | */ |
---|
2191 | 2184 | |
---|
| 2185 | +static int netlink_dump_done(struct netlink_sock *nlk, struct sk_buff *skb, |
---|
| 2186 | + struct netlink_callback *cb, |
---|
| 2187 | + struct netlink_ext_ack *extack) |
---|
| 2188 | +{ |
---|
| 2189 | + struct nlmsghdr *nlh; |
---|
| 2190 | + |
---|
| 2191 | + nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(nlk->dump_done_errno), |
---|
| 2192 | + NLM_F_MULTI | cb->answer_flags); |
---|
| 2193 | + if (WARN_ON(!nlh)) |
---|
| 2194 | + return -ENOBUFS; |
---|
| 2195 | + |
---|
| 2196 | + nl_dump_check_consistent(cb, nlh); |
---|
| 2197 | + memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, sizeof(nlk->dump_done_errno)); |
---|
| 2198 | + |
---|
| 2199 | + if (extack->_msg && nlk->flags & NETLINK_F_EXT_ACK) { |
---|
| 2200 | + nlh->nlmsg_flags |= NLM_F_ACK_TLVS; |
---|
| 2201 | + if (!nla_put_string(skb, NLMSGERR_ATTR_MSG, extack->_msg)) |
---|
| 2202 | + nlmsg_end(skb, nlh); |
---|
| 2203 | + } |
---|
| 2204 | + |
---|
| 2205 | + return 0; |
---|
| 2206 | +} |
---|
| 2207 | + |
---|
2192 | 2208 | static int netlink_dump(struct sock *sk) |
---|
2193 | 2209 | { |
---|
2194 | 2210 | struct netlink_sock *nlk = nlk_sk(sk); |
---|
| 2211 | + struct netlink_ext_ack extack = {}; |
---|
2195 | 2212 | struct netlink_callback *cb; |
---|
2196 | 2213 | struct sk_buff *skb = NULL; |
---|
2197 | | - struct nlmsghdr *nlh; |
---|
2198 | 2214 | struct module *module; |
---|
2199 | 2215 | int err = -ENOBUFS; |
---|
2200 | 2216 | int alloc_min_size; |
---|
.. | .. |
---|
2241 | 2257 | * single netdev. The outcome is MSG_TRUNC error. |
---|
2242 | 2258 | */ |
---|
2243 | 2259 | skb_reserve(skb, skb_tailroom(skb) - alloc_size); |
---|
| 2260 | + |
---|
| 2261 | + /* Make sure malicious BPF programs can not read unitialized memory |
---|
| 2262 | + * from skb->head -> skb->data |
---|
| 2263 | + */ |
---|
| 2264 | + skb_reset_network_header(skb); |
---|
| 2265 | + skb_reset_mac_header(skb); |
---|
| 2266 | + |
---|
2244 | 2267 | netlink_skb_set_owner_r(skb, sk); |
---|
2245 | 2268 | |
---|
2246 | | - if (nlk->dump_done_errno > 0) |
---|
| 2269 | + if (nlk->dump_done_errno > 0) { |
---|
| 2270 | + cb->extack = &extack; |
---|
2247 | 2271 | nlk->dump_done_errno = cb->dump(skb, cb); |
---|
| 2272 | + cb->extack = NULL; |
---|
| 2273 | + } |
---|
2248 | 2274 | |
---|
2249 | 2275 | if (nlk->dump_done_errno > 0 || |
---|
2250 | 2276 | skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) { |
---|
.. | .. |
---|
2257 | 2283 | return 0; |
---|
2258 | 2284 | } |
---|
2259 | 2285 | |
---|
2260 | | - nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, |
---|
2261 | | - sizeof(nlk->dump_done_errno), NLM_F_MULTI); |
---|
2262 | | - if (WARN_ON(!nlh)) |
---|
| 2286 | + if (netlink_dump_done(nlk, skb, cb, &extack)) |
---|
2263 | 2287 | goto errout_skb; |
---|
2264 | 2288 | |
---|
2265 | | - nl_dump_check_consistent(cb, nlh); |
---|
2266 | | - |
---|
2267 | | - memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, |
---|
2268 | | - sizeof(nlk->dump_done_errno)); |
---|
| 2289 | +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES |
---|
| 2290 | + /* frag_list skb's data is used for compat tasks |
---|
| 2291 | + * and the regular skb's data for normal (non-compat) tasks. |
---|
| 2292 | + * See netlink_recvmsg(). |
---|
| 2293 | + */ |
---|
| 2294 | + if (unlikely(skb_shinfo(skb)->frag_list)) { |
---|
| 2295 | + if (netlink_dump_done(nlk, skb_shinfo(skb)->frag_list, cb, &extack)) |
---|
| 2296 | + goto errout_skb; |
---|
| 2297 | + } |
---|
| 2298 | +#endif |
---|
2269 | 2299 | |
---|
2270 | 2300 | if (sk_filter(sk, skb)) |
---|
2271 | 2301 | kfree_skb(skb); |
---|
.. | .. |
---|
2275 | 2305 | if (cb->done) |
---|
2276 | 2306 | cb->done(cb); |
---|
2277 | 2307 | |
---|
2278 | | - nlk->cb_running = false; |
---|
| 2308 | + WRITE_ONCE(nlk->cb_running, false); |
---|
2279 | 2309 | module = cb->module; |
---|
2280 | 2310 | skb = cb->skb; |
---|
2281 | 2311 | mutex_unlock(nlk->cb_mutex); |
---|
.. | .. |
---|
2293 | 2323 | const struct nlmsghdr *nlh, |
---|
2294 | 2324 | struct netlink_dump_control *control) |
---|
2295 | 2325 | { |
---|
| 2326 | + struct netlink_sock *nlk, *nlk2; |
---|
2296 | 2327 | struct netlink_callback *cb; |
---|
2297 | 2328 | struct sock *sk; |
---|
2298 | | - struct netlink_sock *nlk; |
---|
2299 | 2329 | int ret; |
---|
2300 | 2330 | |
---|
2301 | 2331 | refcount_inc(&skb->users); |
---|
.. | .. |
---|
2329 | 2359 | cb->min_dump_alloc = control->min_dump_alloc; |
---|
2330 | 2360 | cb->skb = skb; |
---|
2331 | 2361 | |
---|
| 2362 | + nlk2 = nlk_sk(NETLINK_CB(skb).sk); |
---|
| 2363 | + cb->strict_check = !!(nlk2->flags & NETLINK_F_STRICT_CHK); |
---|
| 2364 | + |
---|
2332 | 2365 | if (control->start) { |
---|
2333 | 2366 | ret = control->start(cb); |
---|
2334 | 2367 | if (ret) |
---|
2335 | 2368 | goto error_put; |
---|
2336 | 2369 | } |
---|
2337 | 2370 | |
---|
2338 | | - nlk->cb_running = true; |
---|
| 2371 | + WRITE_ONCE(nlk->cb_running, true); |
---|
2339 | 2372 | nlk->dump_done_errno = INT_MAX; |
---|
2340 | 2373 | |
---|
2341 | 2374 | mutex_unlock(nlk->cb_mutex); |
---|
.. | .. |
---|
2382 | 2415 | if (nlk_has_extack && extack && extack->_msg) |
---|
2383 | 2416 | tlvlen += nla_total_size(strlen(extack->_msg) + 1); |
---|
2384 | 2417 | |
---|
2385 | | - if (err) { |
---|
2386 | | - if (!(nlk->flags & NETLINK_F_CAP_ACK)) |
---|
2387 | | - payload += nlmsg_len(nlh); |
---|
2388 | | - else |
---|
2389 | | - flags |= NLM_F_CAPPED; |
---|
2390 | | - if (nlk_has_extack && extack && extack->bad_attr) |
---|
2391 | | - tlvlen += nla_total_size(sizeof(u32)); |
---|
2392 | | - } else { |
---|
| 2418 | + if (err && !(nlk->flags & NETLINK_F_CAP_ACK)) |
---|
| 2419 | + payload += nlmsg_len(nlh); |
---|
| 2420 | + else |
---|
2393 | 2421 | flags |= NLM_F_CAPPED; |
---|
2394 | | - |
---|
2395 | | - if (nlk_has_extack && extack && extack->cookie_len) |
---|
2396 | | - tlvlen += nla_total_size(extack->cookie_len); |
---|
2397 | | - } |
---|
| 2422 | + if (err && nlk_has_extack && extack && extack->bad_attr) |
---|
| 2423 | + tlvlen += nla_total_size(sizeof(u32)); |
---|
| 2424 | + if (nlk_has_extack && extack && extack->cookie_len) |
---|
| 2425 | + tlvlen += nla_total_size(extack->cookie_len); |
---|
| 2426 | + if (err && nlk_has_extack && extack && extack->policy) |
---|
| 2427 | + tlvlen += netlink_policy_dump_attr_size_estimate(extack->policy); |
---|
2398 | 2428 | |
---|
2399 | 2429 | if (tlvlen) |
---|
2400 | 2430 | flags |= NLM_F_ACK_TLVS; |
---|
.. | .. |
---|
2417 | 2447 | WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, |
---|
2418 | 2448 | extack->_msg)); |
---|
2419 | 2449 | } |
---|
2420 | | - if (err) { |
---|
2421 | | - if (extack->bad_attr && |
---|
2422 | | - !WARN_ON((u8 *)extack->bad_attr < in_skb->data || |
---|
2423 | | - (u8 *)extack->bad_attr >= in_skb->data + |
---|
2424 | | - in_skb->len)) |
---|
2425 | | - WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS, |
---|
2426 | | - (u8 *)extack->bad_attr - |
---|
2427 | | - (u8 *)nlh)); |
---|
2428 | | - } else { |
---|
2429 | | - if (extack->cookie_len) |
---|
2430 | | - WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE, |
---|
2431 | | - extack->cookie_len, |
---|
2432 | | - extack->cookie)); |
---|
2433 | | - } |
---|
| 2450 | + if (err && extack->bad_attr && |
---|
| 2451 | + !WARN_ON((u8 *)extack->bad_attr < in_skb->data || |
---|
| 2452 | + (u8 *)extack->bad_attr >= in_skb->data + |
---|
| 2453 | + in_skb->len)) |
---|
| 2454 | + WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS, |
---|
| 2455 | + (u8 *)extack->bad_attr - |
---|
| 2456 | + (u8 *)nlh)); |
---|
| 2457 | + if (extack->cookie_len) |
---|
| 2458 | + WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE, |
---|
| 2459 | + extack->cookie_len, extack->cookie)); |
---|
| 2460 | + if (extack->policy) |
---|
| 2461 | + netlink_policy_dump_write_attr(skb, extack->policy, |
---|
| 2462 | + NLMSGERR_ATTR_POLICY); |
---|
2434 | 2463 | } |
---|
2435 | 2464 | |
---|
2436 | 2465 | nlmsg_end(skb, rep); |
---|
.. | .. |
---|
2532 | 2561 | int link; |
---|
2533 | 2562 | }; |
---|
2534 | 2563 | |
---|
2535 | | -static int netlink_walk_start(struct nl_seq_iter *iter) |
---|
| 2564 | +static void netlink_walk_start(struct nl_seq_iter *iter) |
---|
2536 | 2565 | { |
---|
2537 | | - int err; |
---|
2538 | | - |
---|
2539 | | - err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti, |
---|
2540 | | - GFP_KERNEL); |
---|
2541 | | - if (err) { |
---|
2542 | | - iter->link = MAX_LINKS; |
---|
2543 | | - return err; |
---|
2544 | | - } |
---|
2545 | | - |
---|
| 2566 | + rhashtable_walk_enter(&nl_table[iter->link].hash, &iter->hti); |
---|
2546 | 2567 | rhashtable_walk_start(&iter->hti); |
---|
2547 | | - |
---|
2548 | | - return 0; |
---|
2549 | 2568 | } |
---|
2550 | 2569 | |
---|
2551 | 2570 | static void netlink_walk_stop(struct nl_seq_iter *iter) |
---|
.. | .. |
---|
2561 | 2580 | |
---|
2562 | 2581 | do { |
---|
2563 | 2582 | for (;;) { |
---|
2564 | | - int err; |
---|
2565 | | - |
---|
2566 | 2583 | nlk = rhashtable_walk_next(&iter->hti); |
---|
2567 | 2584 | |
---|
2568 | 2585 | if (IS_ERR(nlk)) { |
---|
.. | .. |
---|
2579 | 2596 | if (++iter->link >= MAX_LINKS) |
---|
2580 | 2597 | return NULL; |
---|
2581 | 2598 | |
---|
2582 | | - err = netlink_walk_start(iter); |
---|
2583 | | - if (err) |
---|
2584 | | - return ERR_PTR(err); |
---|
| 2599 | + netlink_walk_start(iter); |
---|
2585 | 2600 | } |
---|
2586 | 2601 | } while (sock_net(&nlk->sk) != seq_file_net(seq)); |
---|
2587 | 2602 | |
---|
.. | .. |
---|
2589 | 2604 | } |
---|
2590 | 2605 | |
---|
2591 | 2606 | static void *netlink_seq_start(struct seq_file *seq, loff_t *posp) |
---|
| 2607 | + __acquires(RCU) |
---|
2592 | 2608 | { |
---|
2593 | 2609 | struct nl_seq_iter *iter = seq->private; |
---|
2594 | 2610 | void *obj = SEQ_START_TOKEN; |
---|
2595 | 2611 | loff_t pos; |
---|
2596 | | - int err; |
---|
2597 | 2612 | |
---|
2598 | 2613 | iter->link = 0; |
---|
2599 | 2614 | |
---|
2600 | | - err = netlink_walk_start(iter); |
---|
2601 | | - if (err) |
---|
2602 | | - return ERR_PTR(err); |
---|
| 2615 | + netlink_walk_start(iter); |
---|
2603 | 2616 | |
---|
2604 | 2617 | for (pos = *posp; pos && obj && !IS_ERR(obj); pos--) |
---|
2605 | 2618 | obj = __netlink_seq_next(seq); |
---|
.. | .. |
---|
2613 | 2626 | return __netlink_seq_next(seq); |
---|
2614 | 2627 | } |
---|
2615 | 2628 | |
---|
2616 | | -static void netlink_seq_stop(struct seq_file *seq, void *v) |
---|
| 2629 | +static void netlink_native_seq_stop(struct seq_file *seq, void *v) |
---|
2617 | 2630 | { |
---|
2618 | 2631 | struct nl_seq_iter *iter = seq->private; |
---|
2619 | 2632 | |
---|
.. | .. |
---|
2624 | 2637 | } |
---|
2625 | 2638 | |
---|
2626 | 2639 | |
---|
2627 | | -static int netlink_seq_show(struct seq_file *seq, void *v) |
---|
| 2640 | +static int netlink_native_seq_show(struct seq_file *seq, void *v) |
---|
2628 | 2641 | { |
---|
2629 | 2642 | if (v == SEQ_START_TOKEN) { |
---|
2630 | 2643 | seq_puts(seq, |
---|
.. | .. |
---|
2634 | 2647 | struct sock *s = v; |
---|
2635 | 2648 | struct netlink_sock *nlk = nlk_sk(s); |
---|
2636 | 2649 | |
---|
2637 | | - seq_printf(seq, "%pK %-3d %-10u %08x %-8d %-8d %-5d %-8d %-8d %-8lu\n", |
---|
| 2650 | + seq_printf(seq, "%pK %-3d %-10u %08x %-8d %-8d %-5d %-8d %-8u %-8lu\n", |
---|
2638 | 2651 | s, |
---|
2639 | 2652 | s->sk_protocol, |
---|
2640 | 2653 | nlk->portid, |
---|
2641 | 2654 | nlk->groups ? (u32)nlk->groups[0] : 0, |
---|
2642 | 2655 | sk_rmem_alloc_get(s), |
---|
2643 | 2656 | sk_wmem_alloc_get(s), |
---|
2644 | | - nlk->cb_running, |
---|
| 2657 | + READ_ONCE(nlk->cb_running), |
---|
2645 | 2658 | refcount_read(&s->sk_refcnt), |
---|
2646 | 2659 | atomic_read(&s->sk_drops), |
---|
2647 | 2660 | sock_i_ino(s) |
---|
.. | .. |
---|
2650 | 2663 | } |
---|
2651 | 2664 | return 0; |
---|
2652 | 2665 | } |
---|
| 2666 | + |
---|
| 2667 | +#ifdef CONFIG_BPF_SYSCALL |
---|
| 2668 | +struct bpf_iter__netlink { |
---|
| 2669 | + __bpf_md_ptr(struct bpf_iter_meta *, meta); |
---|
| 2670 | + __bpf_md_ptr(struct netlink_sock *, sk); |
---|
| 2671 | +}; |
---|
| 2672 | + |
---|
| 2673 | +DEFINE_BPF_ITER_FUNC(netlink, struct bpf_iter_meta *meta, struct netlink_sock *sk) |
---|
| 2674 | + |
---|
| 2675 | +static int netlink_prog_seq_show(struct bpf_prog *prog, |
---|
| 2676 | + struct bpf_iter_meta *meta, |
---|
| 2677 | + void *v) |
---|
| 2678 | +{ |
---|
| 2679 | + struct bpf_iter__netlink ctx; |
---|
| 2680 | + |
---|
| 2681 | + meta->seq_num--; /* skip SEQ_START_TOKEN */ |
---|
| 2682 | + ctx.meta = meta; |
---|
| 2683 | + ctx.sk = nlk_sk((struct sock *)v); |
---|
| 2684 | + return bpf_iter_run_prog(prog, &ctx); |
---|
| 2685 | +} |
---|
| 2686 | + |
---|
| 2687 | +static int netlink_seq_show(struct seq_file *seq, void *v) |
---|
| 2688 | +{ |
---|
| 2689 | + struct bpf_iter_meta meta; |
---|
| 2690 | + struct bpf_prog *prog; |
---|
| 2691 | + |
---|
| 2692 | + meta.seq = seq; |
---|
| 2693 | + prog = bpf_iter_get_info(&meta, false); |
---|
| 2694 | + if (!prog) |
---|
| 2695 | + return netlink_native_seq_show(seq, v); |
---|
| 2696 | + |
---|
| 2697 | + if (v != SEQ_START_TOKEN) |
---|
| 2698 | + return netlink_prog_seq_show(prog, &meta, v); |
---|
| 2699 | + |
---|
| 2700 | + return 0; |
---|
| 2701 | +} |
---|
| 2702 | + |
---|
| 2703 | +static void netlink_seq_stop(struct seq_file *seq, void *v) |
---|
| 2704 | +{ |
---|
| 2705 | + struct bpf_iter_meta meta; |
---|
| 2706 | + struct bpf_prog *prog; |
---|
| 2707 | + |
---|
| 2708 | + if (!v) { |
---|
| 2709 | + meta.seq = seq; |
---|
| 2710 | + prog = bpf_iter_get_info(&meta, true); |
---|
| 2711 | + if (prog) |
---|
| 2712 | + (void)netlink_prog_seq_show(prog, &meta, v); |
---|
| 2713 | + } |
---|
| 2714 | + |
---|
| 2715 | + netlink_native_seq_stop(seq, v); |
---|
| 2716 | +} |
---|
| 2717 | +#else |
---|
| 2718 | +static int netlink_seq_show(struct seq_file *seq, void *v) |
---|
| 2719 | +{ |
---|
| 2720 | + return netlink_native_seq_show(seq, v); |
---|
| 2721 | +} |
---|
| 2722 | + |
---|
| 2723 | +static void netlink_seq_stop(struct seq_file *seq, void *v) |
---|
| 2724 | +{ |
---|
| 2725 | + netlink_native_seq_stop(seq, v); |
---|
| 2726 | +} |
---|
| 2727 | +#endif |
---|
2653 | 2728 | |
---|
2654 | 2729 | static const struct seq_operations netlink_seq_ops = { |
---|
2655 | 2730 | .start = netlink_seq_start, |
---|
.. | .. |
---|
2757 | 2832 | .automatic_shrinking = true, |
---|
2758 | 2833 | }; |
---|
2759 | 2834 | |
---|
| 2835 | +#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) |
---|
| 2836 | +BTF_ID_LIST(btf_netlink_sock_id) |
---|
| 2837 | +BTF_ID(struct, netlink_sock) |
---|
| 2838 | + |
---|
| 2839 | +static const struct bpf_iter_seq_info netlink_seq_info = { |
---|
| 2840 | + .seq_ops = &netlink_seq_ops, |
---|
| 2841 | + .init_seq_private = bpf_iter_init_seq_net, |
---|
| 2842 | + .fini_seq_private = bpf_iter_fini_seq_net, |
---|
| 2843 | + .seq_priv_size = sizeof(struct nl_seq_iter), |
---|
| 2844 | +}; |
---|
| 2845 | + |
---|
| 2846 | +static struct bpf_iter_reg netlink_reg_info = { |
---|
| 2847 | + .target = "netlink", |
---|
| 2848 | + .ctx_arg_info_size = 1, |
---|
| 2849 | + .ctx_arg_info = { |
---|
| 2850 | + { offsetof(struct bpf_iter__netlink, sk), |
---|
| 2851 | + PTR_TO_BTF_ID_OR_NULL }, |
---|
| 2852 | + }, |
---|
| 2853 | + .seq_info = &netlink_seq_info, |
---|
| 2854 | +}; |
---|
| 2855 | + |
---|
| 2856 | +static int __init bpf_iter_register(void) |
---|
| 2857 | +{ |
---|
| 2858 | + netlink_reg_info.ctx_arg_info[0].btf_id = *btf_netlink_sock_id; |
---|
| 2859 | + return bpf_iter_reg_target(&netlink_reg_info); |
---|
| 2860 | +} |
---|
| 2861 | +#endif |
---|
| 2862 | + |
---|
2760 | 2863 | static int __init netlink_proto_init(void) |
---|
2761 | 2864 | { |
---|
2762 | 2865 | int i; |
---|
.. | .. |
---|
2765 | 2868 | if (err != 0) |
---|
2766 | 2869 | goto out; |
---|
2767 | 2870 | |
---|
2768 | | - BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb)); |
---|
| 2871 | +#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) |
---|
| 2872 | + err = bpf_iter_register(); |
---|
| 2873 | + if (err) |
---|
| 2874 | + goto out; |
---|
| 2875 | +#endif |
---|
| 2876 | + |
---|
| 2877 | + BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > sizeof_field(struct sk_buff, cb)); |
---|
2769 | 2878 | |
---|
2770 | 2879 | nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL); |
---|
2771 | 2880 | if (!nl_table) |
---|
.. | .. |
---|
2794 | 2903 | panic("netlink_init: Cannot allocate nl_table\n"); |
---|
2795 | 2904 | } |
---|
2796 | 2905 | |
---|
2797 | | -#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT |
---|
2798 | | -core_initcall_sync(netlink_proto_init); |
---|
2799 | | -#else |
---|
2800 | 2906 | core_initcall(netlink_proto_init); |
---|
2801 | | -#endif |
---|