diff options
Diffstat (limited to 'net/llc/af_llc.c')
| -rw-r--r-- | net/llc/af_llc.c | 184 |
1 files changed, 128 insertions, 56 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 6b8a103cf9e..0080d2b0a8a 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/rtnetlink.h> #include <linux/init.h> +#include <linux/slab.h> #include <net/llc.h> #include <net/llc_sap.h> #include <net/llc_pdu.h> @@ -47,6 +48,10 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout); #define dprintk(args...) #endif +/* Maybe we'll add some more in the future. */ +#define LLC_CMSG_PKTINFO 1 + + /** * llc_ui_next_link_no - return the next unused link number for a sap * @sap: Address of sap to get link number from. @@ -66,8 +71,7 @@ static inline u16 llc_ui_next_link_no(int sap) */ static inline __be16 llc_proto_type(u16 arphrd) { - return arphrd == ARPHRD_IEEE802_TR ? - htons(ETH_P_TR_802_2) : htons(ETH_P_802_2); + return htons(ETH_P_802_2); } /** @@ -103,7 +107,6 @@ static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr) * llc_ui_send_data - send data via reliable llc2 connection * @sk: Connection the socket is using. * @skb: Data the user wishes to send. - * @addr: Source and destination fields provided by the user. * @noblock: can we block waiting for data? * * Send data via reliable llc2 connection. @@ -128,10 +131,8 @@ static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock) static void llc_ui_sk_init(struct socket *sock, struct sock *sk) { + sock_graft(sk, sock); sk->sk_type = sock->type; - sk->sk_sleep = &sock->wait; - sk->sk_socket = sock; - sock->sk = sk; sock->ops = &llc_ui_ops; } @@ -139,25 +140,35 @@ static struct proto llc_proto = { .name = "LLC", .owner = THIS_MODULE, .obj_size = sizeof(struct llc_sock), + .slab_flags = SLAB_DESTROY_BY_RCU, }; /** * llc_ui_create - alloc and init a new llc_ui socket + * @net: network namespace (must be default network) * @sock: Socket to initialize and attach allocated sk to. * @protocol: Unused. + * @kern: on behalf of kernel or userspace * * Allocate and initialize a new llc_ui socket, validate the user wants a * socket type we have available. * Returns 0 upon success, negative upon failure. */ -static int llc_ui_create(struct socket *sock, int protocol) +static int llc_ui_create(struct net *net, struct socket *sock, int protocol, + int kern) { struct sock *sk; int rc = -ESOCKTNOSUPPORT; + if (!ns_capable(net->user_ns, CAP_NET_RAW)) + return -EPERM; + + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) { rc = -ENOMEM; - sk = llc_sk_alloc(PF_LLC, GFP_KERNEL, &llc_proto); + sk = llc_sk_alloc(net, PF_LLC, GFP_KERNEL, &llc_proto); if (sk) { rc = 0; llc_ui_sk_init(sock, sk); @@ -182,14 +193,12 @@ static int llc_ui_release(struct socket *sock) sock_hold(sk); lock_sock(sk); llc = llc_sk(sk); - dprintk("%s: closing local(%02X) remote(%02X)\n", __FUNCTION__, + dprintk("%s: closing local(%02X) remote(%02X)\n", __func__, llc->laddr.lsap, llc->daddr.lsap); if (!llc_send_disc(sk)) llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo); - if (!sock_flag(sk, SOCK_ZAPPED)) { - llc_sap_put(llc->sap); + if (!sock_flag(sk, SOCK_ZAPPED)) llc_sap_remove_socket(llc->sap, sk); - } release_sock(sk); if (llc->dev) dev_put(llc->dev); @@ -249,7 +258,14 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) if (!sock_flag(sk, SOCK_ZAPPED)) goto out; rc = -ENODEV; - llc->dev = dev_getfirstbyhwtype(addr->sllc_arphrd); + if (sk->sk_bound_dev_if) { + llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if); + if (llc->dev && addr->sllc_arphrd != llc->dev->type) { + dev_put(llc->dev); + llc->dev = NULL; + } + } else + llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd); if (!llc->dev) goto out; rc = -EUSERS; @@ -292,16 +308,35 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) struct llc_sap *sap; int rc = -EINVAL; - dprintk("%s: binding %02X\n", __FUNCTION__, addr->sllc_sap); + dprintk("%s: binding %02X\n", __func__, addr->sllc_sap); if (unlikely(!sock_flag(sk, SOCK_ZAPPED) || addrlen != sizeof(*addr))) goto out; rc = -EAFNOSUPPORT; if (unlikely(addr->sllc_family != AF_LLC)) goto out; rc = -ENODEV; - rtnl_lock(); - llc->dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_mac); - rtnl_unlock(); + rcu_read_lock(); + if (sk->sk_bound_dev_if) { + llc->dev = dev_get_by_index_rcu(&init_net, sk->sk_bound_dev_if); + if (llc->dev) { + if (!addr->sllc_arphrd) + addr->sllc_arphrd = llc->dev->type; + if (is_zero_ether_addr(addr->sllc_mac)) + memcpy(addr->sllc_mac, llc->dev->dev_addr, + IFHWADDRLEN); + if (addr->sllc_arphrd != llc->dev->type || + !ether_addr_equal(addr->sllc_mac, + llc->dev->dev_addr)) { + rc = -EINVAL; + llc->dev = NULL; + } + } + } else + llc->dev = dev_getbyhwaddr_rcu(&init_net, addr->sllc_arphrd, + addr->sllc_mac); + if (llc->dev) + dev_hold(llc->dev); + rcu_read_unlock(); if (!llc->dev) goto out; if (!addr->sllc_sap) { @@ -316,7 +351,6 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) rc = -EBUSY; /* some other network layer is using the sap */ if (!sap) goto out; - llc_sap_hold(sap); } else { struct llc_addr laddr, daddr; struct sock *ask; @@ -429,7 +463,7 @@ static int llc_ui_connect(struct socket *sock, struct sockaddr *uaddr, rc = llc_establish_connection(sk, llc->dev->dev_addr, addr->sllc_mac, addr->sllc_sap); if (rc) { - dprintk("%s: llc_ui_send_conn failed :-(\n", __FUNCTION__); + dprintk("%s: llc_ui_send_conn failed :-(\n", __func__); sock->state = SS_UNCONNECTED; sk->sk_state = TCP_CLOSE; goto out; @@ -483,7 +517,7 @@ static int llc_ui_listen(struct socket *sock, int backlog) if (sock_flag(sk, SOCK_ZAPPED)) goto out; rc = 0; - if (!(unsigned)backlog) /* BSDism */ + if (!(unsigned int)backlog) /* BSDism */ backlog = 1; sk->sk_max_ack_backlog = backlog; if (sk->sk_state != TCP_LISTEN) { @@ -502,7 +536,7 @@ static int llc_ui_wait_for_disc(struct sock *sk, long timeout) int rc = 0; while (1) { - prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); if (sk_wait_event(sk, &timeout, sk->sk_state == TCP_CLOSE)) break; rc = -ERESTARTSYS; @@ -513,7 +547,7 @@ static int llc_ui_wait_for_disc(struct sock *sk, long timeout) break; rc = 0; } - finish_wait(sk->sk_sleep, &wait); + finish_wait(sk_sleep(sk), &wait); return rc; } @@ -522,13 +556,13 @@ static int llc_ui_wait_for_conn(struct sock *sk, long timeout) DEFINE_WAIT(wait); while (1) { - prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); if (sk_wait_event(sk, &timeout, sk->sk_state != TCP_SYN_SENT)) break; if (signal_pending(current) || !timeout) break; } - finish_wait(sk->sk_sleep, &wait); + finish_wait(sk_sleep(sk), &wait); return timeout; } @@ -539,7 +573,7 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout) int rc; while (1) { - prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); rc = 0; if (sk_wait_event(sk, &timeout, (sk->sk_shutdown & RCV_SHUTDOWN) || @@ -554,7 +588,7 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout) if (!timeout) break; } - finish_wait(sk->sk_sleep, &wait); + finish_wait(sk_sleep(sk), &wait); return rc; } @@ -585,6 +619,20 @@ static int llc_wait_data(struct sock *sk, long timeo) return rc; } +static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb) +{ + struct llc_sock *llc = llc_sk(skb->sk); + + if (llc->cmsg_flags & LLC_CMSG_PKTINFO) { + struct llc_pktinfo info; + + info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex; + llc_pdu_decode_dsap(skb, &info.lpi_sap); + llc_pdu_decode_da(skb, info.lpi_mac); + put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info); + } +} + /** * llc_ui_accept - accept a new incoming connection. * @sock: Socket which connections arrive on. @@ -601,7 +649,7 @@ static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags) struct sk_buff *skb; int rc = -EOPNOTSUPP; - dprintk("%s: accepting on %02X\n", __FUNCTION__, + dprintk("%s: accepting on %02X\n", __func__, llc_sk(sk)->laddr.lsap); lock_sock(sk); if (unlikely(sk->sk_type != SOCK_STREAM)) @@ -616,7 +664,7 @@ static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags) if (rc) goto out; } - dprintk("%s: got a new connection on %02X\n", __FUNCTION__, + dprintk("%s: got a new connection on %02X\n", __func__, llc_sk(sk)->laddr.lsap); skb = skb_dequeue(&sk->sk_receive_queue); rc = -EINVAL; @@ -637,7 +685,7 @@ static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags) /* put original socket back into a clean listen state. */ sk->sk_state = TCP_LISTEN; sk->sk_ack_backlog--; - dprintk("%s: ok success on %02X, client on %02X\n", __FUNCTION__, + dprintk("%s: ok success on %02X, client on %02X\n", __func__, llc_sk(sk)->addr.sllc_sap, newllc->daddr.lsap); frees: kfree_skb(skb); @@ -659,14 +707,15 @@ out: static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { - struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name; + DECLARE_SOCKADDR(struct sockaddr_llc *, uaddr, msg->msg_name); const int nonblock = flags & MSG_DONTWAIT; struct sk_buff *skb = NULL; struct sock *sk = sock->sk; struct llc_sock *llc = llc_sk(sk); + unsigned long cpu_flags; size_t copied = 0; u32 peek_seq = 0; - u32 *seq; + u32 *seq, skb_len; unsigned long used; int target; /* Read at least this many bytes */ long timeo; @@ -756,14 +805,14 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, sk_wait_data(sk, &timeo); if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) { - if (net_ratelimit()) - printk(KERN_DEBUG "LLC(%s:%d): Application " - "bug, race in MSG_PEEK.\n", - current->comm, current->pid); + net_dbg_ratelimited("LLC(%s:%d): Application bug, race in MSG_PEEK\n", + current->comm, + task_pid_nr(current)); peek_seq = llc->copied_seq; } continue; found_ok_skb: + skb_len = skb->len; /* Ok so how much can we use? */ used = skb->len - offset; if (len < used) @@ -784,17 +833,19 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, copied += used; len -= used; - if (!(flags & MSG_PEEK)) { - sk_eat_skb(sk, skb, 0); - *seq = 0; - } - /* For non stream protcols we get one packet per recvmsg call */ if (sk->sk_type != SOCK_STREAM) goto copy_uaddr; + if (!(flags & MSG_PEEK)) { + spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); + sk_eat_skb(sk, skb, false); + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); + *seq = 0; + } + /* Partial read */ - if (used + offset < skb->len) + if (used + offset < skb_len) continue; } while (len > 0); @@ -806,6 +857,16 @@ copy_uaddr: memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr)); msg->msg_namelen = sizeof(*uaddr); } + if (llc_sk(sk)->cmsg_flags) + llc_cmsg_rcv(msg, skb); + + if (!(flags & MSG_PEEK)) { + spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); + sk_eat_skb(sk, skb, false); + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); + *seq = 0; + } + goto out; } @@ -823,14 +884,14 @@ static int llc_ui_sendmsg(struct kiocb *iocb, struct socket *sock, { struct sock *sk = sock->sk; struct llc_sock *llc = llc_sk(sk); - struct sockaddr_llc *addr = (struct sockaddr_llc *)msg->msg_name; + DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name); int flags = msg->msg_flags; int noblock = flags & MSG_DONTWAIT; struct sk_buff *skb; size_t size = 0; int rc = -EINVAL, copied = 0, hdrlen; - dprintk("%s: sending from %02X to %02X\n", __FUNCTION__, + dprintk("%s: sending from %02X to %02X\n", __func__, llc->laddr.lsap, llc->daddr.lsap); lock_sock(sk); if (addr) { @@ -888,7 +949,7 @@ out: kfree_skb(skb); release: dprintk("%s: failed sending from %02X to %02X: %d\n", - __FUNCTION__, llc->laddr.lsap, llc->daddr.lsap, rc); + __func__, llc->laddr.lsap, llc->daddr.lsap, rc); } release_sock(sk); return rc ? : copied; @@ -909,13 +970,13 @@ static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr, struct sockaddr_llc sllc; struct sock *sk = sock->sk; struct llc_sock *llc = llc_sk(sk); - int rc = 0; + int rc = -EBADF; + memset(&sllc, 0, sizeof(sllc)); lock_sock(sk); if (sock_flag(sk, SOCK_ZAPPED)) goto out; *uaddrlen = sizeof(sllc); - memset(uaddr, 0, *uaddrlen); if (peer) { rc = -ENOTCONN; if (sk->sk_state != TCP_ESTABLISHED) @@ -932,7 +993,7 @@ static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr, if (llc->dev) { sllc.sllc_arphrd = llc->dev->type; - memcpy(&sllc.sllc_mac, &llc->dev->dev_addr, + memcpy(&sllc.sllc_mac, llc->dev->dev_addr, IFHWADDRLEN); } } @@ -963,17 +1024,18 @@ static int llc_ui_ioctl(struct socket *sock, unsigned int cmd, * @sock: Socket to set options on. * @level: Socket level user is requesting operations on. * @optname: Operation name. - * @optval User provided operation data. + * @optval: User provided operation data. * @optlen: Length of optval. * * Set various connection specific parameters. */ static int llc_ui_setsockopt(struct socket *sock, int level, int optname, - char __user *optval, int optlen) + char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct llc_sock *llc = llc_sk(sk); - int rc = -EINVAL, opt; + unsigned int opt; + int rc = -EINVAL; lock_sock(sk); if (unlikely(level != SOL_LLC || optlen != sizeof(int))) @@ -1023,6 +1085,12 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname, goto out; llc->rw = opt; break; + case LLC_OPT_PKTINFO: + if (opt) + llc->cmsg_flags |= LLC_CMSG_PKTINFO; + else + llc->cmsg_flags &= ~LLC_CMSG_PKTINFO; + break; default: rc = -ENOPROTOOPT; goto out; @@ -1076,6 +1144,9 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname, val = llc->k; break; case LLC_OPT_RX_WIN: val = llc->rw; break; + case LLC_OPT_PKTINFO: + val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0; + break; default: rc = -ENOPROTOOPT; goto out; @@ -1088,7 +1159,7 @@ out: return rc; } -static struct net_proto_family llc_ui_family_ops = { +static const struct net_proto_family llc_ui_family_ops = { .family = PF_LLC, .create = llc_ui_create, .owner = THIS_MODULE, @@ -1115,11 +1186,11 @@ static const struct proto_ops llc_ui_ops = { .sendpage = sock_no_sendpage, }; -static char llc_proc_err_msg[] __initdata = +static const char llc_proc_err_msg[] __initconst = KERN_CRIT "LLC: Unable to register the proc_fs entries\n"; -static char llc_sysctl_err_msg[] __initdata = +static const char llc_sysctl_err_msg[] __initconst = KERN_CRIT "LLC: Unable to register the sysctl entries\n"; -static char llc_sock_err_msg[] __initdata = +static const char llc_sock_err_msg[] __initconst = KERN_CRIT "LLC: Unable to register the network family\n"; static int __init llc2_init(void) @@ -1135,7 +1206,7 @@ static int __init llc2_init(void) rc = llc_proc_init(); if (rc != 0) { printk(llc_proc_err_msg); - goto out_unregister_llc_proto; + goto out_station; } rc = llc_sysctl_init(); if (rc) { @@ -1155,7 +1226,8 @@ out_sysctl: llc_sysctl_exit(); out_proc: llc_proc_exit(); -out_unregister_llc_proto: +out_station: + llc_station_exit(); proto_unregister(&llc_proto); goto out; } |
