diff options
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/rtnetlink.c | 342 |
1 files changed, 335 insertions, 7 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 25ca219154e0..06c0c5afabf0 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -243,6 +243,143 @@ void rtnl_unregister_all(int protocol) | |||
243 | 243 | ||
244 | EXPORT_SYMBOL_GPL(rtnl_unregister_all); | 244 | EXPORT_SYMBOL_GPL(rtnl_unregister_all); |
245 | 245 | ||
246 | static LIST_HEAD(link_ops); | ||
247 | |||
248 | /** | ||
249 | * __rtnl_link_register - Register rtnl_link_ops with rtnetlink. | ||
250 | * @ops: struct rtnl_link_ops * to register | ||
251 | * | ||
252 | * The caller must hold the rtnl_mutex. This function should be used | ||
253 | * by drivers that create devices during module initialization. It | ||
254 | * must be called before registering the devices. | ||
255 | * | ||
256 | * Returns 0 on success or a negative error code. | ||
257 | */ | ||
258 | int __rtnl_link_register(struct rtnl_link_ops *ops) | ||
259 | { | ||
260 | list_add_tail(&ops->list, &link_ops); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | EXPORT_SYMBOL_GPL(__rtnl_link_register); | ||
265 | |||
266 | /** | ||
267 | * rtnl_link_register - Register rtnl_link_ops with rtnetlink. | ||
268 | * @ops: struct rtnl_link_ops * to register | ||
269 | * | ||
270 | * Returns 0 on success or a negative error code. | ||
271 | */ | ||
272 | int rtnl_link_register(struct rtnl_link_ops *ops) | ||
273 | { | ||
274 | int err; | ||
275 | |||
276 | rtnl_lock(); | ||
277 | err = __rtnl_link_register(ops); | ||
278 | rtnl_unlock(); | ||
279 | return err; | ||
280 | } | ||
281 | |||
282 | EXPORT_SYMBOL_GPL(rtnl_link_register); | ||
283 | |||
284 | /** | ||
285 | * __rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. | ||
286 | * @ops: struct rtnl_link_ops * to unregister | ||
287 | * | ||
288 | * The caller must hold the rtnl_mutex. This function should be used | ||
289 | * by drivers that unregister devices during module unloading. It must | ||
290 | * be called after unregistering the devices. | ||
291 | */ | ||
292 | void __rtnl_link_unregister(struct rtnl_link_ops *ops) | ||
293 | { | ||
294 | list_del(&ops->list); | ||
295 | } | ||
296 | |||
297 | EXPORT_SYMBOL_GPL(__rtnl_link_unregister); | ||
298 | |||
299 | /** | ||
300 | * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. | ||
301 | * @ops: struct rtnl_link_ops * to unregister | ||
302 | */ | ||
303 | void rtnl_link_unregister(struct rtnl_link_ops *ops) | ||
304 | { | ||
305 | rtnl_lock(); | ||
306 | __rtnl_link_unregister(ops); | ||
307 | rtnl_unlock(); | ||
308 | } | ||
309 | |||
310 | EXPORT_SYMBOL_GPL(rtnl_link_unregister); | ||
311 | |||
312 | static const struct rtnl_link_ops *rtnl_link_ops_get(const char *kind) | ||
313 | { | ||
314 | const struct rtnl_link_ops *ops; | ||
315 | |||
316 | list_for_each_entry(ops, &link_ops, list) { | ||
317 | if (!strcmp(ops->kind, kind)) | ||
318 | return ops; | ||
319 | } | ||
320 | return NULL; | ||
321 | } | ||
322 | |||
323 | static size_t rtnl_link_get_size(const struct net_device *dev) | ||
324 | { | ||
325 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; | ||
326 | size_t size; | ||
327 | |||
328 | if (!ops) | ||
329 | return 0; | ||
330 | |||
331 | size = nlmsg_total_size(sizeof(struct nlattr)) + /* IFLA_LINKINFO */ | ||
332 | nlmsg_total_size(strlen(ops->kind) + 1); /* IFLA_INFO_KIND */ | ||
333 | |||
334 | if (ops->get_size) | ||
335 | /* IFLA_INFO_DATA + nested data */ | ||
336 | size += nlmsg_total_size(sizeof(struct nlattr)) + | ||
337 | ops->get_size(dev); | ||
338 | |||
339 | if (ops->get_xstats_size) | ||
340 | size += ops->get_xstats_size(dev); /* IFLA_INFO_XSTATS */ | ||
341 | |||
342 | return size; | ||
343 | } | ||
344 | |||
345 | static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev) | ||
346 | { | ||
347 | const struct rtnl_link_ops *ops = dev->rtnl_link_ops; | ||
348 | struct nlattr *linkinfo, *data; | ||
349 | int err = -EMSGSIZE; | ||
350 | |||
351 | linkinfo = nla_nest_start(skb, IFLA_LINKINFO); | ||
352 | if (linkinfo == NULL) | ||
353 | goto out; | ||
354 | |||
355 | if (nla_put_string(skb, IFLA_INFO_KIND, ops->kind) < 0) | ||
356 | goto err_cancel_link; | ||
357 | if (ops->fill_xstats) { | ||
358 | err = ops->fill_xstats(skb, dev); | ||
359 | if (err < 0) | ||
360 | goto err_cancel_link; | ||
361 | } | ||
362 | if (ops->fill_info) { | ||
363 | data = nla_nest_start(skb, IFLA_INFO_DATA); | ||
364 | if (data == NULL) | ||
365 | goto err_cancel_link; | ||
366 | err = ops->fill_info(skb, dev); | ||
367 | if (err < 0) | ||
368 | goto err_cancel_data; | ||
369 | nla_nest_end(skb, data); | ||
370 | } | ||
371 | |||
372 | nla_nest_end(skb, linkinfo); | ||
373 | return 0; | ||
374 | |||
375 | err_cancel_data: | ||
376 | nla_nest_cancel(skb, data); | ||
377 | err_cancel_link: | ||
378 | nla_nest_cancel(skb, linkinfo); | ||
379 | out: | ||
380 | return err; | ||
381 | } | ||
382 | |||
246 | static const int rtm_min[RTM_NR_FAMILIES] = | 383 | static const int rtm_min[RTM_NR_FAMILIES] = |
247 | { | 384 | { |
248 | [RTM_FAM(RTM_NEWLINK)] = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | 385 | [RTM_FAM(RTM_NEWLINK)] = NLMSG_LENGTH(sizeof(struct ifinfomsg)), |
@@ -437,7 +574,7 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a, | |||
437 | a->tx_compressed = b->tx_compressed; | 574 | a->tx_compressed = b->tx_compressed; |
438 | }; | 575 | }; |
439 | 576 | ||
440 | static inline size_t if_nlmsg_size(void) | 577 | static inline size_t if_nlmsg_size(const struct net_device *dev) |
441 | { | 578 | { |
442 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) | 579 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) |
443 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ | 580 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ |
@@ -452,7 +589,8 @@ static inline size_t if_nlmsg_size(void) | |||
452 | + nla_total_size(4) /* IFLA_LINK */ | 589 | + nla_total_size(4) /* IFLA_LINK */ |
453 | + nla_total_size(4) /* IFLA_MASTER */ | 590 | + nla_total_size(4) /* IFLA_MASTER */ |
454 | + nla_total_size(1) /* IFLA_OPERSTATE */ | 591 | + nla_total_size(1) /* IFLA_OPERSTATE */ |
455 | + nla_total_size(1); /* IFLA_LINKMODE */ | 592 | + nla_total_size(1) /* IFLA_LINKMODE */ |
593 | + rtnl_link_get_size(dev); /* IFLA_LINKINFO */ | ||
456 | } | 594 | } |
457 | 595 | ||
458 | static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | 596 | static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, |
@@ -522,6 +660,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
522 | } | 660 | } |
523 | } | 661 | } |
524 | 662 | ||
663 | if (dev->rtnl_link_ops) { | ||
664 | if (rtnl_link_fill(skb, dev) < 0) | ||
665 | goto nla_put_failure; | ||
666 | } | ||
667 | |||
525 | return nlmsg_end(skb, nlh); | 668 | return nlmsg_end(skb, nlh); |
526 | 669 | ||
527 | nla_put_failure: | 670 | nla_put_failure: |
@@ -553,6 +696,8 @@ cont: | |||
553 | 696 | ||
554 | static const struct nla_policy ifla_policy[IFLA_MAX+1] = { | 697 | static const struct nla_policy ifla_policy[IFLA_MAX+1] = { |
555 | [IFLA_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ-1 }, | 698 | [IFLA_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ-1 }, |
699 | [IFLA_ADDRESS] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, | ||
700 | [IFLA_BROADCAST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, | ||
556 | [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, | 701 | [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, |
557 | [IFLA_MTU] = { .type = NLA_U32 }, | 702 | [IFLA_MTU] = { .type = NLA_U32 }, |
558 | [IFLA_TXQLEN] = { .type = NLA_U32 }, | 703 | [IFLA_TXQLEN] = { .type = NLA_U32 }, |
@@ -561,10 +706,15 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { | |||
561 | [IFLA_LINKMODE] = { .type = NLA_U8 }, | 706 | [IFLA_LINKMODE] = { .type = NLA_U8 }, |
562 | }; | 707 | }; |
563 | 708 | ||
709 | static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { | ||
710 | [IFLA_INFO_KIND] = { .type = NLA_STRING }, | ||
711 | [IFLA_INFO_DATA] = { .type = NLA_NESTED }, | ||
712 | }; | ||
713 | |||
564 | static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | 714 | static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, |
565 | struct nlattr **tb, char *ifname) | 715 | struct nlattr **tb, char *ifname, int modified) |
566 | { | 716 | { |
567 | int modified = 0, send_addr_notify = 0; | 717 | int send_addr_notify = 0; |
568 | int err; | 718 | int err; |
569 | 719 | ||
570 | if (tb[IFLA_MAP]) { | 720 | if (tb[IFLA_MAP]) { |
@@ -729,13 +879,189 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
729 | nla_len(tb[IFLA_BROADCAST]) < dev->addr_len) | 879 | nla_len(tb[IFLA_BROADCAST]) < dev->addr_len) |
730 | goto errout_dev; | 880 | goto errout_dev; |
731 | 881 | ||
732 | err = do_setlink(dev, ifm, tb, ifname); | 882 | err = do_setlink(dev, ifm, tb, ifname, 0); |
733 | errout_dev: | 883 | errout_dev: |
734 | dev_put(dev); | 884 | dev_put(dev); |
735 | errout: | 885 | errout: |
736 | return err; | 886 | return err; |
737 | } | 887 | } |
738 | 888 | ||
889 | static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
890 | { | ||
891 | const struct rtnl_link_ops *ops; | ||
892 | struct net_device *dev; | ||
893 | struct ifinfomsg *ifm; | ||
894 | char ifname[IFNAMSIZ]; | ||
895 | struct nlattr *tb[IFLA_MAX+1]; | ||
896 | int err; | ||
897 | |||
898 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); | ||
899 | if (err < 0) | ||
900 | return err; | ||
901 | |||
902 | if (tb[IFLA_IFNAME]) | ||
903 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); | ||
904 | |||
905 | ifm = nlmsg_data(nlh); | ||
906 | if (ifm->ifi_index > 0) | ||
907 | dev = __dev_get_by_index(ifm->ifi_index); | ||
908 | else if (tb[IFLA_IFNAME]) | ||
909 | dev = __dev_get_by_name(ifname); | ||
910 | else | ||
911 | return -EINVAL; | ||
912 | |||
913 | if (!dev) | ||
914 | return -ENODEV; | ||
915 | |||
916 | ops = dev->rtnl_link_ops; | ||
917 | if (!ops) | ||
918 | return -EOPNOTSUPP; | ||
919 | |||
920 | ops->dellink(dev); | ||
921 | return 0; | ||
922 | } | ||
923 | |||
924 | static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
925 | { | ||
926 | const struct rtnl_link_ops *ops; | ||
927 | struct net_device *dev; | ||
928 | struct ifinfomsg *ifm; | ||
929 | char kind[MODULE_NAME_LEN]; | ||
930 | char ifname[IFNAMSIZ]; | ||
931 | struct nlattr *tb[IFLA_MAX+1]; | ||
932 | struct nlattr *linkinfo[IFLA_INFO_MAX+1]; | ||
933 | int err; | ||
934 | |||
935 | replay: | ||
936 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); | ||
937 | if (err < 0) | ||
938 | return err; | ||
939 | |||
940 | if (tb[IFLA_IFNAME]) | ||
941 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); | ||
942 | else | ||
943 | ifname[0] = '\0'; | ||
944 | |||
945 | ifm = nlmsg_data(nlh); | ||
946 | if (ifm->ifi_index > 0) | ||
947 | dev = __dev_get_by_index(ifm->ifi_index); | ||
948 | else if (ifname[0]) | ||
949 | dev = __dev_get_by_name(ifname); | ||
950 | else | ||
951 | dev = NULL; | ||
952 | |||
953 | if (tb[IFLA_LINKINFO]) { | ||
954 | err = nla_parse_nested(linkinfo, IFLA_INFO_MAX, | ||
955 | tb[IFLA_LINKINFO], ifla_info_policy); | ||
956 | if (err < 0) | ||
957 | return err; | ||
958 | } else | ||
959 | memset(linkinfo, 0, sizeof(linkinfo)); | ||
960 | |||
961 | if (linkinfo[IFLA_INFO_KIND]) { | ||
962 | nla_strlcpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind)); | ||
963 | ops = rtnl_link_ops_get(kind); | ||
964 | } else { | ||
965 | kind[0] = '\0'; | ||
966 | ops = NULL; | ||
967 | } | ||
968 | |||
969 | if (1) { | ||
970 | struct nlattr *attr[ops ? ops->maxtype + 1 : 0], **data = NULL; | ||
971 | |||
972 | if (ops) { | ||
973 | if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) { | ||
974 | err = nla_parse_nested(attr, ops->maxtype, | ||
975 | linkinfo[IFLA_INFO_DATA], | ||
976 | ops->policy); | ||
977 | if (err < 0) | ||
978 | return err; | ||
979 | data = attr; | ||
980 | } | ||
981 | if (ops->validate) { | ||
982 | err = ops->validate(tb, data); | ||
983 | if (err < 0) | ||
984 | return err; | ||
985 | } | ||
986 | } | ||
987 | |||
988 | if (dev) { | ||
989 | int modified = 0; | ||
990 | |||
991 | if (nlh->nlmsg_flags & NLM_F_EXCL) | ||
992 | return -EEXIST; | ||
993 | if (nlh->nlmsg_flags & NLM_F_REPLACE) | ||
994 | return -EOPNOTSUPP; | ||
995 | |||
996 | if (linkinfo[IFLA_INFO_DATA]) { | ||
997 | if (!ops || ops != dev->rtnl_link_ops || | ||
998 | !ops->changelink) | ||
999 | return -EOPNOTSUPP; | ||
1000 | |||
1001 | err = ops->changelink(dev, tb, data); | ||
1002 | if (err < 0) | ||
1003 | return err; | ||
1004 | modified = 1; | ||
1005 | } | ||
1006 | |||
1007 | return do_setlink(dev, ifm, tb, ifname, modified); | ||
1008 | } | ||
1009 | |||
1010 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) | ||
1011 | return -ENODEV; | ||
1012 | |||
1013 | if (ifm->ifi_index || ifm->ifi_flags || ifm->ifi_change) | ||
1014 | return -EOPNOTSUPP; | ||
1015 | if (tb[IFLA_ADDRESS] || tb[IFLA_BROADCAST] || tb[IFLA_MAP] || | ||
1016 | tb[IFLA_MASTER] || tb[IFLA_PROTINFO]) | ||
1017 | return -EOPNOTSUPP; | ||
1018 | |||
1019 | if (!ops) { | ||
1020 | #ifdef CONFIG_KMOD | ||
1021 | if (kind[0]) { | ||
1022 | __rtnl_unlock(); | ||
1023 | request_module("rtnl-link-%s", kind); | ||
1024 | rtnl_lock(); | ||
1025 | ops = rtnl_link_ops_get(kind); | ||
1026 | if (ops) | ||
1027 | goto replay; | ||
1028 | } | ||
1029 | #endif | ||
1030 | return -EOPNOTSUPP; | ||
1031 | } | ||
1032 | |||
1033 | if (!ifname[0]) | ||
1034 | snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind); | ||
1035 | dev = alloc_netdev(ops->priv_size, ifname, ops->setup); | ||
1036 | if (!dev) | ||
1037 | return -ENOMEM; | ||
1038 | |||
1039 | if (strchr(dev->name, '%')) { | ||
1040 | err = dev_alloc_name(dev, dev->name); | ||
1041 | if (err < 0) | ||
1042 | goto err_free; | ||
1043 | } | ||
1044 | dev->rtnl_link_ops = ops; | ||
1045 | |||
1046 | if (tb[IFLA_MTU]) | ||
1047 | dev->mtu = nla_get_u32(tb[IFLA_MTU]); | ||
1048 | if (tb[IFLA_TXQLEN]) | ||
1049 | dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]); | ||
1050 | if (tb[IFLA_WEIGHT]) | ||
1051 | dev->weight = nla_get_u32(tb[IFLA_WEIGHT]); | ||
1052 | if (tb[IFLA_OPERSTATE]) | ||
1053 | set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE])); | ||
1054 | if (tb[IFLA_LINKMODE]) | ||
1055 | dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]); | ||
1056 | |||
1057 | err = ops->newlink(dev, tb, data); | ||
1058 | err_free: | ||
1059 | if (err < 0) | ||
1060 | free_netdev(dev); | ||
1061 | return err; | ||
1062 | } | ||
1063 | } | ||
1064 | |||
739 | static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | 1065 | static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) |
740 | { | 1066 | { |
741 | struct ifinfomsg *ifm; | 1067 | struct ifinfomsg *ifm; |
@@ -756,7 +1082,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
756 | } else | 1082 | } else |
757 | return -EINVAL; | 1083 | return -EINVAL; |
758 | 1084 | ||
759 | nskb = nlmsg_new(if_nlmsg_size(), GFP_KERNEL); | 1085 | nskb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL); |
760 | if (nskb == NULL) { | 1086 | if (nskb == NULL) { |
761 | err = -ENOBUFS; | 1087 | err = -ENOBUFS; |
762 | goto errout; | 1088 | goto errout; |
@@ -806,7 +1132,7 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) | |||
806 | struct sk_buff *skb; | 1132 | struct sk_buff *skb; |
807 | int err = -ENOBUFS; | 1133 | int err = -ENOBUFS; |
808 | 1134 | ||
809 | skb = nlmsg_new(if_nlmsg_size(), GFP_KERNEL); | 1135 | skb = nlmsg_new(if_nlmsg_size(dev), GFP_KERNEL); |
810 | if (skb == NULL) | 1136 | if (skb == NULL) |
811 | goto errout; | 1137 | goto errout; |
812 | 1138 | ||
@@ -961,6 +1287,8 @@ void __init rtnetlink_init(void) | |||
961 | 1287 | ||
962 | rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo); | 1288 | rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo); |
963 | rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL); | 1289 | rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL); |
1290 | rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL); | ||
1291 | rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL); | ||
964 | 1292 | ||
965 | rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all); | 1293 | rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all); |
966 | rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all); | 1294 | rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all); |