Skip to content

Commit 9a4595b

Browse files
kaberDavid S. Miller
authored andcommitted
[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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f7fa9b1 commit 9a4595b

File tree

3 files changed

+103
-2
lines changed

3 files changed

+103
-2
lines changed

include/linux/netlink.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ struct nlmsgerr
9090
struct nlmsghdr msg;
9191
};
9292

93+
#define NETLINK_ADD_MEMBERSHIP 1
94+
#define NETLINK_DROP_MEMBERSHIP 2
95+
#define NETLINK_PKTINFO 3
96+
97+
struct nl_pktinfo
98+
{
99+
__u32 group;
100+
};
101+
93102
#define NET_MAJOR 36 /* Major 36 is reserved for networking */
94103

95104
enum {

include/linux/socket.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ struct ucred {
272272
#define SOL_NETBEUI 267
273273
#define SOL_LLC 268
274274
#define SOL_DCCP 269
275+
#define SOL_NETLINK 270
275276

276277
/* IPX options */
277278
#define IPX_TYPE 1

net/netlink/af_netlink.c

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ struct netlink_sock {
8181
};
8282

8383
#define NETLINK_KERNEL_SOCKET 0x1
84+
#define NETLINK_RECV_PKTINFO 0x2
8485

8586
static inline struct netlink_sock *nlk_sk(struct sock *sk)
8687
{
@@ -946,6 +947,94 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
946947
read_unlock(&nl_table_lock);
947948
}
948949

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+
9491038
static inline void netlink_rcv_wake(struct sock *sk)
9501039
{
9511040
struct netlink_sock *nlk = nlk_sk(sk);
@@ -1091,6 +1180,8 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
10911180
netlink_dump(sk);
10921181

10931182
scm_recv(sock, msg, siocb->scm, flags);
1183+
if (nlk->flags & NETLINK_RECV_PKTINFO)
1184+
netlink_cmsg_recv_pktinfo(msg, skb);
10941185

10951186
out:
10961187
netlink_rcv_wake(sk);
@@ -1465,8 +1556,8 @@ static struct proto_ops netlink_ops = {
14651556
.ioctl = sock_no_ioctl,
14661557
.listen = sock_no_listen,
14671558
.shutdown = sock_no_shutdown,
1468-
.setsockopt = sock_no_setsockopt,
1469-
.getsockopt = sock_no_getsockopt,
1559+
.setsockopt = netlink_setsockopt,
1560+
.getsockopt = netlink_getsockopt,
14701561
.sendmsg = netlink_sendmsg,
14711562
.recvmsg = netlink_recvmsg,
14721563
.mmap = sock_no_mmap,

0 commit comments

Comments
 (0)