diff options
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/rtnetlink.c | 188 |
1 files changed, 166 insertions, 22 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 33ea8eac7fe..fb1630d82dd 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -50,12 +50,18 @@ | |||
50 | #include <net/sock.h> | 50 | #include <net/sock.h> |
51 | #include <net/pkt_sched.h> | 51 | #include <net/pkt_sched.h> |
52 | #include <net/fib_rules.h> | 52 | #include <net/fib_rules.h> |
53 | #include <net/netlink.h> | 53 | #include <net/rtnetlink.h> |
54 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK | 54 | #ifdef CONFIG_NET_WIRELESS_RTNETLINK |
55 | #include <linux/wireless.h> | 55 | #include <linux/wireless.h> |
56 | #include <net/iw_handler.h> | 56 | #include <net/iw_handler.h> |
57 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | 57 | #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ |
58 | 58 | ||
59 | struct rtnl_link | ||
60 | { | ||
61 | rtnl_doit_func doit; | ||
62 | rtnl_dumpit_func dumpit; | ||
63 | }; | ||
64 | |||
59 | static DEFINE_MUTEX(rtnl_mutex); | 65 | static DEFINE_MUTEX(rtnl_mutex); |
60 | static struct sock *rtnl; | 66 | static struct sock *rtnl; |
61 | 67 | ||
@@ -95,7 +101,151 @@ int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len) | |||
95 | return 0; | 101 | return 0; |
96 | } | 102 | } |
97 | 103 | ||
98 | struct rtnetlink_link * rtnetlink_links[NPROTO]; | 104 | struct rtnl_link *rtnl_msg_handlers[NPROTO]; |
105 | |||
106 | static inline int rtm_msgindex(int msgtype) | ||
107 | { | ||
108 | int msgindex = msgtype - RTM_BASE; | ||
109 | |||
110 | /* | ||
111 | * msgindex < 0 implies someone tried to register a netlink | ||
112 | * control code. msgindex >= RTM_NR_MSGTYPES may indicate that | ||
113 | * the message type has not been added to linux/rtnetlink.h | ||
114 | */ | ||
115 | BUG_ON(msgindex < 0 || msgindex >= RTM_NR_MSGTYPES); | ||
116 | |||
117 | return msgindex; | ||
118 | } | ||
119 | |||
120 | static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex) | ||
121 | { | ||
122 | struct rtnl_link *tab; | ||
123 | |||
124 | tab = rtnl_msg_handlers[protocol]; | ||
125 | if (tab == NULL || tab->doit == NULL) | ||
126 | tab = rtnl_msg_handlers[PF_UNSPEC]; | ||
127 | |||
128 | return tab ? tab->doit : NULL; | ||
129 | } | ||
130 | |||
131 | static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) | ||
132 | { | ||
133 | struct rtnl_link *tab; | ||
134 | |||
135 | tab = rtnl_msg_handlers[protocol]; | ||
136 | if (tab == NULL || tab->dumpit == NULL) | ||
137 | tab = rtnl_msg_handlers[PF_UNSPEC]; | ||
138 | |||
139 | return tab ? tab->dumpit : NULL; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * __rtnl_register - Register a rtnetlink message type | ||
144 | * @protocol: Protocol family or PF_UNSPEC | ||
145 | * @msgtype: rtnetlink message type | ||
146 | * @doit: Function pointer called for each request message | ||
147 | * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message | ||
148 | * | ||
149 | * Registers the specified function pointers (at least one of them has | ||
150 | * to be non-NULL) to be called whenever a request message for the | ||
151 | * specified protocol family and message type is received. | ||
152 | * | ||
153 | * The special protocol family PF_UNSPEC may be used to define fallback | ||
154 | * function pointers for the case when no entry for the specific protocol | ||
155 | * family exists. | ||
156 | * | ||
157 | * Returns 0 on success or a negative error code. | ||
158 | */ | ||
159 | int __rtnl_register(int protocol, int msgtype, | ||
160 | rtnl_doit_func doit, rtnl_dumpit_func dumpit) | ||
161 | { | ||
162 | struct rtnl_link *tab; | ||
163 | int msgindex; | ||
164 | |||
165 | BUG_ON(protocol < 0 || protocol >= NPROTO); | ||
166 | msgindex = rtm_msgindex(msgtype); | ||
167 | |||
168 | tab = rtnl_msg_handlers[protocol]; | ||
169 | if (tab == NULL) { | ||
170 | tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); | ||
171 | if (tab == NULL) | ||
172 | return -ENOBUFS; | ||
173 | |||
174 | rtnl_msg_handlers[protocol] = tab; | ||
175 | } | ||
176 | |||
177 | if (doit) | ||
178 | tab[msgindex].doit = doit; | ||
179 | |||
180 | if (dumpit) | ||
181 | tab[msgindex].dumpit = dumpit; | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | EXPORT_SYMBOL_GPL(__rtnl_register); | ||
187 | |||
188 | /** | ||
189 | * rtnl_register - Register a rtnetlink message type | ||
190 | * | ||
191 | * Identical to __rtnl_register() but panics on failure. This is useful | ||
192 | * as failure of this function is very unlikely, it can only happen due | ||
193 | * to lack of memory when allocating the chain to store all message | ||
194 | * handlers for a protocol. Meant for use in init functions where lack | ||
195 | * of memory implies no sense in continueing. | ||
196 | */ | ||
197 | void rtnl_register(int protocol, int msgtype, | ||
198 | rtnl_doit_func doit, rtnl_dumpit_func dumpit) | ||
199 | { | ||
200 | if (__rtnl_register(protocol, msgtype, doit, dumpit) < 0) | ||
201 | panic("Unable to register rtnetlink message handler, " | ||
202 | "protocol = %d, message type = %d\n", | ||
203 | protocol, msgtype); | ||
204 | } | ||
205 | |||
206 | EXPORT_SYMBOL_GPL(rtnl_register); | ||
207 | |||
208 | /** | ||
209 | * rtnl_unregister - Unregister a rtnetlink message type | ||
210 | * @protocol: Protocol family or PF_UNSPEC | ||
211 | * @msgtype: rtnetlink message type | ||
212 | * | ||
213 | * Returns 0 on success or a negative error code. | ||
214 | */ | ||
215 | int rtnl_unregister(int protocol, int msgtype) | ||
216 | { | ||
217 | int msgindex; | ||
218 | |||
219 | BUG_ON(protocol < 0 || protocol >= NPROTO); | ||
220 | msgindex = rtm_msgindex(msgtype); | ||
221 | |||
222 | if (rtnl_msg_handlers[protocol] == NULL) | ||
223 | return -ENOENT; | ||
224 | |||
225 | rtnl_msg_handlers[protocol][msgindex].doit = NULL; | ||
226 | rtnl_msg_handlers[protocol][msgindex].dumpit = NULL; | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | EXPORT_SYMBOL_GPL(rtnl_unregister); | ||
232 | |||
233 | /** | ||
234 | * rtnl_unregister_all - Unregister all rtnetlink message type of a protocol | ||
235 | * @protocol : Protocol family or PF_UNSPEC | ||
236 | * | ||
237 | * Identical to calling rtnl_unregster() for all registered message types | ||
238 | * of a certain protocol family. | ||
239 | */ | ||
240 | void rtnl_unregister_all(int protocol) | ||
241 | { | ||
242 | BUG_ON(protocol < 0 || protocol >= NPROTO); | ||
243 | |||
244 | kfree(rtnl_msg_handlers[protocol]); | ||
245 | rtnl_msg_handlers[protocol] = NULL; | ||
246 | } | ||
247 | |||
248 | EXPORT_SYMBOL_GPL(rtnl_unregister_all); | ||
99 | 249 | ||
100 | static const int rtm_min[RTM_NR_FAMILIES] = | 250 | static const int rtm_min[RTM_NR_FAMILIES] = |
101 | { | 251 | { |
@@ -648,7 +798,7 @@ errout: | |||
648 | return err; | 798 | return err; |
649 | } | 799 | } |
650 | 800 | ||
651 | static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) | 801 | int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) |
652 | { | 802 | { |
653 | int idx; | 803 | int idx; |
654 | int s_idx = cb->family; | 804 | int s_idx = cb->family; |
@@ -659,12 +809,12 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) | |||
659 | int type = cb->nlh->nlmsg_type-RTM_BASE; | 809 | int type = cb->nlh->nlmsg_type-RTM_BASE; |
660 | if (idx < s_idx || idx == PF_PACKET) | 810 | if (idx < s_idx || idx == PF_PACKET) |
661 | continue; | 811 | continue; |
662 | if (rtnetlink_links[idx] == NULL || | 812 | if (rtnl_msg_handlers[idx] == NULL || |
663 | rtnetlink_links[idx][type].dumpit == NULL) | 813 | rtnl_msg_handlers[idx][type].dumpit == NULL) |
664 | continue; | 814 | continue; |
665 | if (idx > s_idx) | 815 | if (idx > s_idx) |
666 | memset(&cb->args[0], 0, sizeof(cb->args)); | 816 | memset(&cb->args[0], 0, sizeof(cb->args)); |
667 | if (rtnetlink_links[idx][type].dumpit(skb, cb)) | 817 | if (rtnl_msg_handlers[idx][type].dumpit(skb, cb)) |
668 | break; | 818 | break; |
669 | } | 819 | } |
670 | cb->family = idx; | 820 | cb->family = idx; |
@@ -672,6 +822,8 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) | |||
672 | return skb->len; | 822 | return skb->len; |
673 | } | 823 | } |
674 | 824 | ||
825 | EXPORT_SYMBOL_GPL(rtnl_dump_all); | ||
826 | |||
675 | void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) | 827 | void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) |
676 | { | 828 | { |
677 | struct sk_buff *skb; | 829 | struct sk_buff *skb; |
@@ -703,8 +855,7 @@ static int rtattr_max; | |||
703 | static __inline__ int | 855 | static __inline__ int |
704 | rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) | 856 | rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) |
705 | { | 857 | { |
706 | struct rtnetlink_link *link; | 858 | rtnl_doit_func doit; |
707 | struct rtnetlink_link *link_tab; | ||
708 | int sz_idx, kind; | 859 | int sz_idx, kind; |
709 | int min_len; | 860 | int min_len; |
710 | int family; | 861 | int family; |
@@ -737,11 +888,6 @@ rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) | |||
737 | return -1; | 888 | return -1; |
738 | } | 889 | } |
739 | 890 | ||
740 | link_tab = rtnetlink_links[family]; | ||
741 | if (link_tab == NULL) | ||
742 | link_tab = rtnetlink_links[PF_UNSPEC]; | ||
743 | link = &link_tab[type]; | ||
744 | |||
745 | sz_idx = type>>2; | 891 | sz_idx = type>>2; |
746 | kind = type&3; | 892 | kind = type&3; |
747 | 893 | ||
@@ -751,14 +897,14 @@ rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) | |||
751 | } | 897 | } |
752 | 898 | ||
753 | if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { | 899 | if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { |
754 | if (link->dumpit == NULL) | 900 | rtnl_dumpit_func dumpit; |
755 | link = &(rtnetlink_links[PF_UNSPEC][type]); | ||
756 | 901 | ||
757 | if (link->dumpit == NULL) | 902 | dumpit = rtnl_get_dumpit(family, type); |
903 | if (dumpit == NULL) | ||
758 | goto err_inval; | 904 | goto err_inval; |
759 | 905 | ||
760 | if ((*errp = netlink_dump_start(rtnl, skb, nlh, | 906 | if ((*errp = netlink_dump_start(rtnl, skb, nlh, |
761 | link->dumpit, NULL)) != 0) { | 907 | dumpit, NULL)) != 0) { |
762 | return -1; | 908 | return -1; |
763 | } | 909 | } |
764 | 910 | ||
@@ -787,11 +933,10 @@ rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) | |||
787 | } | 933 | } |
788 | } | 934 | } |
789 | 935 | ||
790 | if (link->doit == NULL) | 936 | doit = rtnl_get_doit(family, type); |
791 | link = &(rtnetlink_links[PF_UNSPEC][type]); | 937 | if (doit == NULL) |
792 | if (link->doit == NULL) | ||
793 | goto err_inval; | 938 | goto err_inval; |
794 | err = link->doit(skb, nlh, (void *)&rta_buf[0]); | 939 | err = doit(skb, nlh, (void *)&rta_buf[0]); |
795 | 940 | ||
796 | *errp = err; | 941 | *errp = err; |
797 | return err; | 942 | return err; |
@@ -886,7 +1031,6 @@ void __init rtnetlink_init(void) | |||
886 | EXPORT_SYMBOL(__rta_fill); | 1031 | EXPORT_SYMBOL(__rta_fill); |
887 | EXPORT_SYMBOL(rtattr_strlcpy); | 1032 | EXPORT_SYMBOL(rtattr_strlcpy); |
888 | EXPORT_SYMBOL(rtattr_parse); | 1033 | EXPORT_SYMBOL(rtattr_parse); |
889 | EXPORT_SYMBOL(rtnetlink_links); | ||
890 | EXPORT_SYMBOL(rtnetlink_put_metrics); | 1034 | EXPORT_SYMBOL(rtnetlink_put_metrics); |
891 | EXPORT_SYMBOL(rtnl_lock); | 1035 | EXPORT_SYMBOL(rtnl_lock); |
892 | EXPORT_SYMBOL(rtnl_trylock); | 1036 | EXPORT_SYMBOL(rtnl_trylock); |