diff options
author | Patrick McHardy <kaber@trash.net> | 2005-08-15 15:32:15 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-08-29 19:01:07 -0400 |
commit | 9a4595bc7e67962f13232ee55a64e063062c3a99 (patch) | |
tree | 9691d77701cad7e0d4fb62390acf525f3adb9d60 /net/netlink | |
parent | f7fa9b10edbb9391bdd4ec8e8b3d621d0664b198 (diff) |
[NETLINK]: Add set/getsockopt options to support more than 32 groups
NETLINK_ADD_MEMBERSHIP/NETLINK_DROP_MEMBERSHIP are used to join/leave
groups, NETLINK_PKTINFO is used to enable nl_pktinfo control messages
for received packets to get the extended destination group number.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 95 |
1 files changed, 93 insertions, 2 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 58d4ca42ac32..47e791738014 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -81,6 +81,7 @@ struct netlink_sock { | |||
81 | }; | 81 | }; |
82 | 82 | ||
83 | #define NETLINK_KERNEL_SOCKET 0x1 | 83 | #define NETLINK_KERNEL_SOCKET 0x1 |
84 | #define NETLINK_RECV_PKTINFO 0x2 | ||
84 | 85 | ||
85 | static inline struct netlink_sock *nlk_sk(struct sock *sk) | 86 | static inline struct netlink_sock *nlk_sk(struct sock *sk) |
86 | { | 87 | { |
@@ -946,6 +947,94 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) | |||
946 | read_unlock(&nl_table_lock); | 947 | read_unlock(&nl_table_lock); |
947 | } | 948 | } |
948 | 949 | ||
950 | static int netlink_setsockopt(struct socket *sock, int level, int optname, | ||
951 | char __user *optval, int optlen) | ||
952 | { | ||
953 | struct sock *sk = sock->sk; | ||
954 | struct netlink_sock *nlk = nlk_sk(sk); | ||
955 | int val = 0, err; | ||
956 | |||
957 | if (level != SOL_NETLINK) | ||
958 | return -ENOPROTOOPT; | ||
959 | |||
960 | if (optlen >= sizeof(int) && | ||
961 | get_user(val, (int __user *)optval)) | ||
962 | return -EFAULT; | ||
963 | |||
964 | switch (optname) { | ||
965 | case NETLINK_PKTINFO: | ||
966 | if (val) | ||
967 | nlk->flags |= NETLINK_RECV_PKTINFO; | ||
968 | else | ||
969 | nlk->flags &= ~NETLINK_RECV_PKTINFO; | ||
970 | err = 0; | ||
971 | break; | ||
972 | case NETLINK_ADD_MEMBERSHIP: | ||
973 | case NETLINK_DROP_MEMBERSHIP: { | ||
974 | unsigned int subscriptions; | ||
975 | int old, new = optname == NETLINK_ADD_MEMBERSHIP ? 1 : 0; | ||
976 | |||
977 | if (!netlink_capable(sock, NL_NONROOT_RECV)) | ||
978 | return -EPERM; | ||
979 | if (!val || val - 1 >= nlk->ngroups) | ||
980 | return -EINVAL; | ||
981 | netlink_table_grab(); | ||
982 | old = test_bit(val - 1, nlk->groups); | ||
983 | subscriptions = nlk->subscriptions - old + new; | ||
984 | if (new) | ||
985 | __set_bit(val - 1, nlk->groups); | ||
986 | else | ||
987 | __clear_bit(val - 1, nlk->groups); | ||
988 | netlink_update_subscriptions(sk, subscriptions); | ||
989 | netlink_table_ungrab(); | ||
990 | err = 0; | ||
991 | break; | ||
992 | } | ||
993 | default: | ||
994 | err = -ENOPROTOOPT; | ||
995 | } | ||
996 | return err; | ||
997 | } | ||
998 | |||
999 | static int netlink_getsockopt(struct socket *sock, int level, int optname, | ||
1000 | char __user *optval, int __user *optlen) | ||
1001 | { | ||
1002 | struct sock *sk = sock->sk; | ||
1003 | struct netlink_sock *nlk = nlk_sk(sk); | ||
1004 | int len, val, err; | ||
1005 | |||
1006 | if (level != SOL_NETLINK) | ||
1007 | return -ENOPROTOOPT; | ||
1008 | |||
1009 | if (get_user(len, optlen)) | ||
1010 | return -EFAULT; | ||
1011 | if (len < 0) | ||
1012 | return -EINVAL; | ||
1013 | |||
1014 | switch (optname) { | ||
1015 | case NETLINK_PKTINFO: | ||
1016 | if (len < sizeof(int)) | ||
1017 | return -EINVAL; | ||
1018 | len = sizeof(int); | ||
1019 | val = nlk->flags & NETLINK_RECV_PKTINFO ? 1 : 0; | ||
1020 | put_user(len, optlen); | ||
1021 | put_user(val, optval); | ||
1022 | err = 0; | ||
1023 | break; | ||
1024 | default: | ||
1025 | err = -ENOPROTOOPT; | ||
1026 | } | ||
1027 | return err; | ||
1028 | } | ||
1029 | |||
1030 | static void netlink_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) | ||
1031 | { | ||
1032 | struct nl_pktinfo info; | ||
1033 | |||
1034 | info.group = NETLINK_CB(skb).dst_group; | ||
1035 | put_cmsg(msg, SOL_NETLINK, NETLINK_PKTINFO, sizeof(info), &info); | ||
1036 | } | ||
1037 | |||
949 | static inline void netlink_rcv_wake(struct sock *sk) | 1038 | static inline void netlink_rcv_wake(struct sock *sk) |
950 | { | 1039 | { |
951 | struct netlink_sock *nlk = nlk_sk(sk); | 1040 | struct netlink_sock *nlk = nlk_sk(sk); |
@@ -1091,6 +1180,8 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, | |||
1091 | netlink_dump(sk); | 1180 | netlink_dump(sk); |
1092 | 1181 | ||
1093 | scm_recv(sock, msg, siocb->scm, flags); | 1182 | scm_recv(sock, msg, siocb->scm, flags); |
1183 | if (nlk->flags & NETLINK_RECV_PKTINFO) | ||
1184 | netlink_cmsg_recv_pktinfo(msg, skb); | ||
1094 | 1185 | ||
1095 | out: | 1186 | out: |
1096 | netlink_rcv_wake(sk); | 1187 | netlink_rcv_wake(sk); |
@@ -1465,8 +1556,8 @@ static struct proto_ops netlink_ops = { | |||
1465 | .ioctl = sock_no_ioctl, | 1556 | .ioctl = sock_no_ioctl, |
1466 | .listen = sock_no_listen, | 1557 | .listen = sock_no_listen, |
1467 | .shutdown = sock_no_shutdown, | 1558 | .shutdown = sock_no_shutdown, |
1468 | .setsockopt = sock_no_setsockopt, | 1559 | .setsockopt = netlink_setsockopt, |
1469 | .getsockopt = sock_no_getsockopt, | 1560 | .getsockopt = netlink_getsockopt, |
1470 | .sendmsg = netlink_sendmsg, | 1561 | .sendmsg = netlink_sendmsg, |
1471 | .recvmsg = netlink_recvmsg, | 1562 | .recvmsg = netlink_recvmsg, |
1472 | .mmap = sock_no_mmap, | 1563 | .mmap = sock_no_mmap, |