diff options
Diffstat (limited to 'net/llc/af_llc.c')
-rw-r--r-- | net/llc/af_llc.c | 74 |
1 files changed, 65 insertions, 9 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 7aa4fd170104..2db6a9f75913 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/rtnetlink.h> | 26 | #include <linux/rtnetlink.h> |
27 | #include <linux/init.h> | 27 | #include <linux/init.h> |
28 | #include <linux/slab.h> | ||
28 | #include <net/llc.h> | 29 | #include <net/llc.h> |
29 | #include <net/llc_sap.h> | 30 | #include <net/llc_sap.h> |
30 | #include <net/llc_pdu.h> | 31 | #include <net/llc_pdu.h> |
@@ -47,6 +48,10 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout); | |||
47 | #define dprintk(args...) | 48 | #define dprintk(args...) |
48 | #endif | 49 | #endif |
49 | 50 | ||
51 | /* Maybe we'll add some more in the future. */ | ||
52 | #define LLC_CMSG_PKTINFO 1 | ||
53 | |||
54 | |||
50 | /** | 55 | /** |
51 | * llc_ui_next_link_no - return the next unused link number for a sap | 56 | * llc_ui_next_link_no - return the next unused link number for a sap |
52 | * @sap: Address of sap to get link number from. | 57 | * @sap: Address of sap to get link number from. |
@@ -136,18 +141,22 @@ static struct proto llc_proto = { | |||
136 | .name = "LLC", | 141 | .name = "LLC", |
137 | .owner = THIS_MODULE, | 142 | .owner = THIS_MODULE, |
138 | .obj_size = sizeof(struct llc_sock), | 143 | .obj_size = sizeof(struct llc_sock), |
144 | .slab_flags = SLAB_DESTROY_BY_RCU, | ||
139 | }; | 145 | }; |
140 | 146 | ||
141 | /** | 147 | /** |
142 | * llc_ui_create - alloc and init a new llc_ui socket | 148 | * llc_ui_create - alloc and init a new llc_ui socket |
149 | * @net: network namespace (must be default network) | ||
143 | * @sock: Socket to initialize and attach allocated sk to. | 150 | * @sock: Socket to initialize and attach allocated sk to. |
144 | * @protocol: Unused. | 151 | * @protocol: Unused. |
152 | * @kern: on behalf of kernel or userspace | ||
145 | * | 153 | * |
146 | * Allocate and initialize a new llc_ui socket, validate the user wants a | 154 | * Allocate and initialize a new llc_ui socket, validate the user wants a |
147 | * socket type we have available. | 155 | * socket type we have available. |
148 | * Returns 0 upon success, negative upon failure. | 156 | * Returns 0 upon success, negative upon failure. |
149 | */ | 157 | */ |
150 | static int llc_ui_create(struct net *net, struct socket *sock, int protocol) | 158 | static int llc_ui_create(struct net *net, struct socket *sock, int protocol, |
159 | int kern) | ||
151 | { | 160 | { |
152 | struct sock *sk; | 161 | struct sock *sk; |
153 | int rc = -ESOCKTNOSUPPORT; | 162 | int rc = -ESOCKTNOSUPPORT; |
@@ -155,7 +164,7 @@ static int llc_ui_create(struct net *net, struct socket *sock, int protocol) | |||
155 | if (!capable(CAP_NET_RAW)) | 164 | if (!capable(CAP_NET_RAW)) |
156 | return -EPERM; | 165 | return -EPERM; |
157 | 166 | ||
158 | if (net != &init_net) | 167 | if (!net_eq(net, &init_net)) |
159 | return -EAFNOSUPPORT; | 168 | return -EAFNOSUPPORT; |
160 | 169 | ||
161 | if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) { | 170 | if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) { |
@@ -189,10 +198,8 @@ static int llc_ui_release(struct socket *sock) | |||
189 | llc->laddr.lsap, llc->daddr.lsap); | 198 | llc->laddr.lsap, llc->daddr.lsap); |
190 | if (!llc_send_disc(sk)) | 199 | if (!llc_send_disc(sk)) |
191 | llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); | 200 | llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); |
192 | if (!sock_flag(sk, SOCK_ZAPPED)) { | 201 | if (!sock_flag(sk, SOCK_ZAPPED)) |
193 | llc_sap_put(llc->sap); | ||
194 | llc_sap_remove_socket(llc->sap, sk); | 202 | llc_sap_remove_socket(llc->sap, sk); |
195 | } | ||
196 | release_sock(sk); | 203 | release_sock(sk); |
197 | if (llc->dev) | 204 | if (llc->dev) |
198 | dev_put(llc->dev); | 205 | dev_put(llc->dev); |
@@ -252,7 +259,14 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) | |||
252 | if (!sock_flag(sk, SOCK_ZAPPED)) | 259 | if (!sock_flag(sk, SOCK_ZAPPED)) |
253 | goto out; | 260 | goto out; |
254 | rc = -ENODEV; | 261 | rc = -ENODEV; |
255 | llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); | 262 | if (sk->sk_bound_dev_if) { |
263 | llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if); | ||
264 | if (llc->dev && addr->sllc_arphrd != llc->dev->type) { | ||
265 | dev_put(llc->dev); | ||
266 | llc->dev = NULL; | ||
267 | } | ||
268 | } else | ||
269 | llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); | ||
256 | if (!llc->dev) | 270 | if (!llc->dev) |
257 | goto out; | 271 | goto out; |
258 | rc = -EUSERS; | 272 | rc = -EUSERS; |
@@ -303,7 +317,25 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) | |||
303 | goto out; | 317 | goto out; |
304 | rc = -ENODEV; | 318 | rc = -ENODEV; |
305 | rtnl_lock(); | 319 | rtnl_lock(); |
306 | llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd, addr->sllc_mac); | 320 | if (sk->sk_bound_dev_if) { |
321 | llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if); | ||
322 | if (llc->dev) { | ||
323 | if (!addr->sllc_arphrd) | ||
324 | addr->sllc_arphrd = llc->dev->type; | ||
325 | if (llc_mac_null(addr->sllc_mac)) | ||
326 | memcpy(addr->sllc_mac, llc->dev->dev_addr, | ||
327 | IFHWADDRLEN); | ||
328 | if (addr->sllc_arphrd != llc->dev->type || | ||
329 | !llc_mac_match(addr->sllc_mac, | ||
330 | llc->dev->dev_addr)) { | ||
331 | rc = -EINVAL; | ||
332 | dev_put(llc->dev); | ||
333 | llc->dev = NULL; | ||
334 | } | ||
335 | } | ||
336 | } else | ||
337 | llc->dev = dev_getbyhwaddr(&init_net, addr->sllc_arphrd, | ||
338 | addr->sllc_mac); | ||
307 | rtnl_unlock(); | 339 | rtnl_unlock(); |
308 | if (!llc->dev) | 340 | if (!llc->dev) |
309 | goto out; | 341 | goto out; |
@@ -319,7 +351,6 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) | |||
319 | rc = -EBUSY; /* some other network layer is using the sap */ | 351 | rc = -EBUSY; /* some other network layer is using the sap */ |
320 | if (!sap) | 352 | if (!sap) |
321 | goto out; | 353 | goto out; |
322 | llc_sap_hold(sap); | ||
323 | } else { | 354 | } else { |
324 | struct llc_addr laddr, daddr; | 355 | struct llc_addr laddr, daddr; |
325 | struct sock *ask; | 356 | struct sock *ask; |
@@ -588,6 +619,20 @@ static int llc_wait_data(struct sock *sk, long timeo) | |||
588 | return rc; | 619 | return rc; |
589 | } | 620 | } |
590 | 621 | ||
622 | static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb) | ||
623 | { | ||
624 | struct llc_sock *llc = llc_sk(skb->sk); | ||
625 | |||
626 | if (llc->cmsg_flags & LLC_CMSG_PKTINFO) { | ||
627 | struct llc_pktinfo info; | ||
628 | |||
629 | info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex; | ||
630 | llc_pdu_decode_dsap(skb, &info.lpi_sap); | ||
631 | llc_pdu_decode_da(skb, info.lpi_mac); | ||
632 | put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info); | ||
633 | } | ||
634 | } | ||
635 | |||
591 | /** | 636 | /** |
592 | * llc_ui_accept - accept a new incoming connection. | 637 | * llc_ui_accept - accept a new incoming connection. |
593 | * @sock: Socket which connections arrive on. | 638 | * @sock: Socket which connections arrive on. |
@@ -809,6 +854,8 @@ copy_uaddr: | |||
809 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); | 854 | memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); |
810 | msg->msg_namelen = sizeof(*uaddr); | 855 | msg->msg_namelen = sizeof(*uaddr); |
811 | } | 856 | } |
857 | if (llc_sk(sk)->cmsg_flags) | ||
858 | llc_cmsg_rcv(msg, skb); | ||
812 | goto out; | 859 | goto out; |
813 | } | 860 | } |
814 | 861 | ||
@@ -1027,6 +1074,12 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname, | |||
1027 | goto out; | 1074 | goto out; |
1028 | llc->rw = opt; | 1075 | llc->rw = opt; |
1029 | break; | 1076 | break; |
1077 | case LLC_OPT_PKTINFO: | ||
1078 | if (opt) | ||
1079 | llc->cmsg_flags |= LLC_CMSG_PKTINFO; | ||
1080 | else | ||
1081 | llc->cmsg_flags &= ~LLC_CMSG_PKTINFO; | ||
1082 | break; | ||
1030 | default: | 1083 | default: |
1031 | rc = -ENOPROTOOPT; | 1084 | rc = -ENOPROTOOPT; |
1032 | goto out; | 1085 | goto out; |
@@ -1080,6 +1133,9 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname, | |||
1080 | val = llc->k; break; | 1133 | val = llc->k; break; |
1081 | case LLC_OPT_RX_WIN: | 1134 | case LLC_OPT_RX_WIN: |
1082 | val = llc->rw; break; | 1135 | val = llc->rw; break; |
1136 | case LLC_OPT_PKTINFO: | ||
1137 | val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0; | ||
1138 | break; | ||
1083 | default: | 1139 | default: |
1084 | rc = -ENOPROTOOPT; | 1140 | rc = -ENOPROTOOPT; |
1085 | goto out; | 1141 | goto out; |
@@ -1092,7 +1148,7 @@ out: | |||
1092 | return rc; | 1148 | return rc; |
1093 | } | 1149 | } |
1094 | 1150 | ||
1095 | static struct net_proto_family llc_ui_family_ops = { | 1151 | static const struct net_proto_family llc_ui_family_ops = { |
1096 | .family = PF_LLC, | 1152 | .family = PF_LLC, |
1097 | .create = llc_ui_create, | 1153 | .create = llc_ui_create, |
1098 | .owner = THIS_MODULE, | 1154 | .owner = THIS_MODULE, |