diff options
author | Roopa Prabhu <roopa@cumulusnetworks.com> | 2018-12-19 15:51:38 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-12-19 16:37:34 -0500 |
commit | 82cbb5c631a07b3aa6df6eab644d55da9de5a645 (patch) | |
tree | f071d0fa16682ef217525ff8236c3713b884934d | |
parent | 4ab0edecaf1d9a4acb5bddc4a869b0f7efda634a (diff) |
neighbour: register rtnl doit handler
this patch registers neigh doit handler. The doit handler
returns a neigh entry given dst and dev. This is similar
to route and fdb doit (get) handlers. Also moves nda_policy
declaration from rtnetlink.c to neighbour.c
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Reviewed-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/neighbour.h | 1 | ||||
-rw-r--r-- | net/core/neighbour.c | 204 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 12 |
3 files changed, 194 insertions, 23 deletions
diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 66221f1991c0..7c1ab9edba03 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h | |||
@@ -255,6 +255,7 @@ static inline void *neighbour_priv(const struct neighbour *n) | |||
255 | #define NEIGH_UPDATE_F_ISROUTER 0x40000000 | 255 | #define NEIGH_UPDATE_F_ISROUTER 0x40000000 |
256 | #define NEIGH_UPDATE_F_ADMIN 0x80000000 | 256 | #define NEIGH_UPDATE_F_ADMIN 0x80000000 |
257 | 257 | ||
258 | extern const struct nla_policy nda_policy[]; | ||
258 | 259 | ||
259 | static inline bool neigh_key_eq16(const struct neighbour *n, const void *pkey) | 260 | static inline bool neigh_key_eq16(const struct neighbour *n, const void *pkey) |
260 | { | 261 | { |
diff --git a/net/core/neighbour.c b/net/core/neighbour.c index fb4372cb1de1..43687c9abe1d 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c | |||
@@ -1751,6 +1751,18 @@ static struct neigh_table *neigh_find_table(int family) | |||
1751 | return tbl; | 1751 | return tbl; |
1752 | } | 1752 | } |
1753 | 1753 | ||
1754 | const struct nla_policy nda_policy[NDA_MAX+1] = { | ||
1755 | [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, | ||
1756 | [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, | ||
1757 | [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, | ||
1758 | [NDA_PROBES] = { .type = NLA_U32 }, | ||
1759 | [NDA_VLAN] = { .type = NLA_U16 }, | ||
1760 | [NDA_PORT] = { .type = NLA_U16 }, | ||
1761 | [NDA_VNI] = { .type = NLA_U32 }, | ||
1762 | [NDA_IFINDEX] = { .type = NLA_U32 }, | ||
1763 | [NDA_MASTER] = { .type = NLA_U32 }, | ||
1764 | }; | ||
1765 | |||
1754 | static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, | 1766 | static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, |
1755 | struct netlink_ext_ack *extack) | 1767 | struct netlink_ext_ack *extack) |
1756 | { | 1768 | { |
@@ -2711,6 +2723,186 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) | |||
2711 | return skb->len; | 2723 | return skb->len; |
2712 | } | 2724 | } |
2713 | 2725 | ||
2726 | static int neigh_valid_get_req(const struct nlmsghdr *nlh, | ||
2727 | struct neigh_table **tbl, | ||
2728 | void **dst, int *dev_idx, u8 *ndm_flags, | ||
2729 | struct netlink_ext_ack *extack) | ||
2730 | { | ||
2731 | struct nlattr *tb[NDA_MAX + 1]; | ||
2732 | struct ndmsg *ndm; | ||
2733 | int err, i; | ||
2734 | |||
2735 | if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { | ||
2736 | NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request"); | ||
2737 | return -EINVAL; | ||
2738 | } | ||
2739 | |||
2740 | ndm = nlmsg_data(nlh); | ||
2741 | if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || | ||
2742 | ndm->ndm_type) { | ||
2743 | NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request"); | ||
2744 | return -EINVAL; | ||
2745 | } | ||
2746 | |||
2747 | if (ndm->ndm_flags & ~NTF_PROXY) { | ||
2748 | NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request"); | ||
2749 | return -EINVAL; | ||
2750 | } | ||
2751 | |||
2752 | err = nlmsg_parse_strict(nlh, sizeof(struct ndmsg), tb, NDA_MAX, | ||
2753 | nda_policy, extack); | ||
2754 | if (err < 0) | ||
2755 | return err; | ||
2756 | |||
2757 | *ndm_flags = ndm->ndm_flags; | ||
2758 | *dev_idx = ndm->ndm_ifindex; | ||
2759 | *tbl = neigh_find_table(ndm->ndm_family); | ||
2760 | if (*tbl == NULL) { | ||
2761 | NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); | ||
2762 | return -EAFNOSUPPORT; | ||
2763 | } | ||
2764 | |||
2765 | for (i = 0; i <= NDA_MAX; ++i) { | ||
2766 | if (!tb[i]) | ||
2767 | continue; | ||
2768 | |||
2769 | switch (i) { | ||
2770 | case NDA_DST: | ||
2771 | if (nla_len(tb[i]) != (int)(*tbl)->key_len) { | ||
2772 | NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); | ||
2773 | return -EINVAL; | ||
2774 | } | ||
2775 | *dst = nla_data(tb[i]); | ||
2776 | break; | ||
2777 | default: | ||
2778 | NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request"); | ||
2779 | return -EINVAL; | ||
2780 | } | ||
2781 | } | ||
2782 | |||
2783 | return 0; | ||
2784 | } | ||
2785 | |||
2786 | static inline size_t neigh_nlmsg_size(void) | ||
2787 | { | ||
2788 | return NLMSG_ALIGN(sizeof(struct ndmsg)) | ||
2789 | + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ | ||
2790 | + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ | ||
2791 | + nla_total_size(sizeof(struct nda_cacheinfo)) | ||
2792 | + nla_total_size(4) /* NDA_PROBES */ | ||
2793 | + nla_total_size(1); /* NDA_PROTOCOL */ | ||
2794 | } | ||
2795 | |||
2796 | static int neigh_get_reply(struct net *net, struct neighbour *neigh, | ||
2797 | u32 pid, u32 seq) | ||
2798 | { | ||
2799 | struct sk_buff *skb; | ||
2800 | int err = 0; | ||
2801 | |||
2802 | skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL); | ||
2803 | if (!skb) | ||
2804 | return -ENOBUFS; | ||
2805 | |||
2806 | err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0); | ||
2807 | if (err) { | ||
2808 | kfree_skb(skb); | ||
2809 | goto errout; | ||
2810 | } | ||
2811 | |||
2812 | err = rtnl_unicast(skb, net, pid); | ||
2813 | errout: | ||
2814 | return err; | ||
2815 | } | ||
2816 | |||
2817 | static inline size_t pneigh_nlmsg_size(void) | ||
2818 | { | ||
2819 | return NLMSG_ALIGN(sizeof(struct ndmsg)) | ||
2820 | + nla_total_size(MAX_ADDR_LEN); /* NDA_DST */ | ||
2821 | + nla_total_size(1); /* NDA_PROTOCOL */ | ||
2822 | } | ||
2823 | |||
2824 | static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh, | ||
2825 | u32 pid, u32 seq, struct neigh_table *tbl) | ||
2826 | { | ||
2827 | struct sk_buff *skb; | ||
2828 | int err = 0; | ||
2829 | |||
2830 | skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL); | ||
2831 | if (!skb) | ||
2832 | return -ENOBUFS; | ||
2833 | |||
2834 | err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl); | ||
2835 | if (err) { | ||
2836 | kfree_skb(skb); | ||
2837 | goto errout; | ||
2838 | } | ||
2839 | |||
2840 | err = rtnl_unicast(skb, net, pid); | ||
2841 | errout: | ||
2842 | return err; | ||
2843 | } | ||
2844 | |||
2845 | static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, | ||
2846 | struct netlink_ext_ack *extack) | ||
2847 | { | ||
2848 | struct net *net = sock_net(in_skb->sk); | ||
2849 | struct net_device *dev = NULL; | ||
2850 | struct neigh_table *tbl = NULL; | ||
2851 | struct neighbour *neigh; | ||
2852 | void *dst = NULL; | ||
2853 | u8 ndm_flags = 0; | ||
2854 | int dev_idx = 0; | ||
2855 | int err; | ||
2856 | |||
2857 | err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags, | ||
2858 | extack); | ||
2859 | if (err < 0) | ||
2860 | return err; | ||
2861 | |||
2862 | if (dev_idx) { | ||
2863 | dev = __dev_get_by_index(net, dev_idx); | ||
2864 | if (!dev) { | ||
2865 | NL_SET_ERR_MSG(extack, "Unknown device ifindex"); | ||
2866 | return -ENODEV; | ||
2867 | } | ||
2868 | } | ||
2869 | |||
2870 | if (!dst) { | ||
2871 | NL_SET_ERR_MSG(extack, "Network address not specified"); | ||
2872 | return -EINVAL; | ||
2873 | } | ||
2874 | |||
2875 | if (ndm_flags & NTF_PROXY) { | ||
2876 | struct pneigh_entry *pn; | ||
2877 | |||
2878 | pn = pneigh_lookup(tbl, net, dst, dev, 0); | ||
2879 | if (!pn) { | ||
2880 | NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found"); | ||
2881 | return -ENOENT; | ||
2882 | } | ||
2883 | return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid, | ||
2884 | nlh->nlmsg_seq, tbl); | ||
2885 | } | ||
2886 | |||
2887 | if (!dev) { | ||
2888 | NL_SET_ERR_MSG(extack, "No device specified"); | ||
2889 | return -EINVAL; | ||
2890 | } | ||
2891 | |||
2892 | neigh = neigh_lookup(tbl, dst, dev); | ||
2893 | if (!neigh) { | ||
2894 | NL_SET_ERR_MSG(extack, "Neighbour entry not found"); | ||
2895 | return -ENOENT; | ||
2896 | } | ||
2897 | |||
2898 | err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid, | ||
2899 | nlh->nlmsg_seq); | ||
2900 | |||
2901 | neigh_release(neigh); | ||
2902 | |||
2903 | return err; | ||
2904 | } | ||
2905 | |||
2714 | void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) | 2906 | void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) |
2715 | { | 2907 | { |
2716 | int chain; | 2908 | int chain; |
@@ -3118,16 +3310,6 @@ static const struct seq_operations neigh_stat_seq_ops = { | |||
3118 | }; | 3310 | }; |
3119 | #endif /* CONFIG_PROC_FS */ | 3311 | #endif /* CONFIG_PROC_FS */ |
3120 | 3312 | ||
3121 | static inline size_t neigh_nlmsg_size(void) | ||
3122 | { | ||
3123 | return NLMSG_ALIGN(sizeof(struct ndmsg)) | ||
3124 | + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ | ||
3125 | + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ | ||
3126 | + nla_total_size(sizeof(struct nda_cacheinfo)) | ||
3127 | + nla_total_size(4) /* NDA_PROBES */ | ||
3128 | + nla_total_size(1); /* NDA_PROTOCOL */ | ||
3129 | } | ||
3130 | |||
3131 | static void __neigh_notify(struct neighbour *n, int type, int flags, | 3313 | static void __neigh_notify(struct neighbour *n, int type, int flags, |
3132 | u32 pid) | 3314 | u32 pid) |
3133 | { | 3315 | { |
@@ -3511,7 +3693,7 @@ static int __init neigh_init(void) | |||
3511 | { | 3693 | { |
3512 | rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); | 3694 | rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); |
3513 | rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); | 3695 | rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); |
3514 | rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, 0); | 3696 | rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0); |
3515 | 3697 | ||
3516 | rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, | 3698 | rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, |
3517 | 0); | 3699 | 0); |
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index baf2685b4da2..48f61885fd6f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -3460,18 +3460,6 @@ void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change, | |||
3460 | new_nsid, new_ifindex); | 3460 | new_nsid, new_ifindex); |
3461 | } | 3461 | } |
3462 | 3462 | ||
3463 | static const struct nla_policy nda_policy[NDA_MAX+1] = { | ||
3464 | [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, | ||
3465 | [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, | ||
3466 | [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, | ||
3467 | [NDA_PROBES] = { .type = NLA_U32 }, | ||
3468 | [NDA_VLAN] = { .type = NLA_U16 }, | ||
3469 | [NDA_PORT] = { .type = NLA_U16 }, | ||
3470 | [NDA_VNI] = { .type = NLA_U32 }, | ||
3471 | [NDA_IFINDEX] = { .type = NLA_U32 }, | ||
3472 | [NDA_MASTER] = { .type = NLA_U32 }, | ||
3473 | }; | ||
3474 | |||
3475 | static int nlmsg_populate_fdb_fill(struct sk_buff *skb, | 3463 | static int nlmsg_populate_fdb_fill(struct sk_buff *skb, |
3476 | struct net_device *dev, | 3464 | struct net_device *dev, |
3477 | u8 *addr, u16 vid, u32 pid, u32 seq, | 3465 | u8 *addr, u16 vid, u32 pid, u32 seq, |