diff options
Diffstat (limited to 'net/x25/af_x25.c')
| -rw-r--r-- | net/x25/af_x25.c | 855 |
1 files changed, 528 insertions, 327 deletions
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 52a2726d327..5ad4418ef09 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -3,7 +3,7 @@ * * This is ALPHA test software. This code may break your machine, * randomly fail to work with new releases, misbehave and/or generally - * screw up. It might even work. + * screw up. It might even work. * * This code REQUIRES 2.1.15 or higher * @@ -18,11 +18,11 @@ * X.25 002 Jonathan Naylor Centralised disconnect handling. * New timer architecture. * 2000-03-11 Henner Eisen MSG_EOR handling more POSIX compliant. - * 2000-03-22 Daniela Squassoni Allowed disabling/enabling of - * facilities negotiation and increased + * 2000-03-22 Daniela Squassoni Allowed disabling/enabling of + * facilities negotiation and increased * the throughput upper limit. * 2000-08-27 Arnaldo C. Melo s/suser/capable/ + micro cleanups - * 2000-09-04 Henner Eisen Set sock->state in x25_accept(). + * 2000-09-04 Henner Eisen Set sock->state in x25_accept(). * Fixed x25_output() related skb leakage. * 2000-10-02 Henner Eisen Made x25_kick() single threaded per socket. * 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation. @@ -35,6 +35,8 @@ * response */ +#define pr_fmt(fmt) "X25: " fmt + #include <linux/module.h> #include <linux/capability.h> #include <linux/errno.h> @@ -46,6 +48,7 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/skbuff.h> +#include <linux/slab.h> #include <net/sock.h> #include <net/tcp_states.h> #include <asm/uaccess.h> @@ -54,6 +57,7 @@ #include <linux/notifier.h> #include <linux/init.h> #include <linux/compat.h> +#include <linux/ctype.h> #include <net/x25.h> #include <net/compat.h> @@ -63,6 +67,7 @@ int sysctl_x25_call_request_timeout = X25_DEFAULT_T21; int sysctl_x25_reset_request_timeout = X25_DEFAULT_T22; int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23; int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; +int sysctl_x25_forward = 0; HLIST_HEAD(x25_list); DEFINE_RWLOCK(x25_list_lock); @@ -79,12 +84,47 @@ struct compat_x25_subscrip_struct { }; #endif + +int x25_parse_address_block(struct sk_buff *skb, + struct x25_address *called_addr, + struct x25_address *calling_addr) +{ + unsigned char len; + int needed; + int rc; + + if (!pskb_may_pull(skb, 1)) { + /* packet has no address block */ + rc = 0; + goto empty; + } + + len = *skb->data; + needed = 1 + (len >> 4) + (len & 0x0f); + + if (!pskb_may_pull(skb, needed)) { + /* packet is too short to hold the addresses it claims + to hold */ + rc = -1; + goto empty; + } + + return x25_addr_ntoa(skb->data, called_addr, calling_addr); + +empty: + *called_addr->x25_addr = 0; + *calling_addr->x25_addr = 0; + + return rc; +} + + int x25_addr_ntoa(unsigned char *p, struct x25_address *called_addr, struct x25_address *calling_addr) { - int called_len, calling_len; + unsigned int called_len, calling_len; char *called, *calling; - int i; + unsigned int i; called_len = (*p >> 0) & 0x0F; calling_len = (*p >> 4) & 0x0F; @@ -170,11 +210,10 @@ static void x25_remove_socket(struct sock *sk) static void x25_kill_by_device(struct net_device *dev) { struct sock *s; - struct hlist_node *node; write_lock_bh(&x25_list_lock); - sk_for_each(s, node, &x25_list) + sk_for_each(s, &x25_list) if (x25_sk(s)->neighbour && x25_sk(s)->neighbour->dev == dev) x25_disconnect(s, ENETUNREACH, 0, 0); @@ -187,30 +226,33 @@ static void x25_kill_by_device(struct net_device *dev) static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct x25_neigh *nb; + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + if (dev->type == ARPHRD_X25 -#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) +#if IS_ENABLED(CONFIG_LLC) || dev->type == ARPHRD_ETHER #endif ) { switch (event) { - case NETDEV_UP: - x25_link_device_up(dev); - break; - case NETDEV_GOING_DOWN: - nb = x25_get_neigh(dev); - if (nb) { - x25_terminate_link(nb); - x25_neigh_put(nb); - } - break; - case NETDEV_DOWN: - x25_kill_by_device(dev); - x25_route_device_down(dev); - x25_link_device_down(dev); - break; + case NETDEV_UP: + x25_link_device_up(dev); + break; + case NETDEV_GOING_DOWN: + nb = x25_get_neigh(dev); + if (nb) { + x25_terminate_link(nb); + x25_neigh_put(nb); + } + break; + case NETDEV_DOWN: + x25_kill_by_device(dev); + x25_route_device_down(dev); + x25_link_device_down(dev); + break; } } @@ -239,12 +281,11 @@ static struct sock *x25_find_listener(struct x25_address *addr, { struct sock *s; struct sock *next_best; - struct hlist_node *node; read_lock_bh(&x25_list_lock); next_best = NULL; - sk_for_each(s, node, &x25_list) + sk_for_each(s, &x25_list) if ((!strcmp(addr->x25_addr, x25_sk(s)->source_addr.x25_addr) || !strcmp(addr->x25_addr, @@ -254,9 +295,10 @@ static struct sock *x25_find_listener(struct x25_address *addr, * Found a listening socket, now check the incoming * call user data vs this sockets call user data */ - if(skb->len > 0 && x25_sk(s)->cudmatchlength > 0) { - if((memcmp(x25_sk(s)->calluserdata.cuddata, - skb->data, + if (x25_sk(s)->cudmatchlength > 0 && + skb->len >= x25_sk(s)->cudmatchlength) { + if((memcmp(x25_sk(s)->calluserdata.cuddata, + skb->data, x25_sk(s)->cudmatchlength)) == 0) { sock_hold(s); goto found; @@ -281,9 +323,8 @@ found: static struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb) { struct sock *s; - struct hlist_node *node; - sk_for_each(s, node, &x25_list) + sk_for_each(s, &x25_list) if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == nb) { sock_hold(s); goto found; @@ -328,14 +369,14 @@ static unsigned int x25_new_lci(struct x25_neigh *nb) /* * Deferred destroy. */ -void x25_destroy_socket(struct sock *); +static void __x25_destroy_socket(struct sock *); /* * handler for deferred kills. */ static void x25_destroy_timer(unsigned long data) { - x25_destroy_socket((struct sock *)data); + x25_destroy_socket_from_timer((struct sock *)data); } /* @@ -345,12 +386,10 @@ static void x25_destroy_timer(unsigned long data) * will touch it and we are (fairly 8-) ) safe. * Not static as it's used by the timer */ -void x25_destroy_socket(struct sock *sk) +static void __x25_destroy_socket(struct sock *sk) { struct sk_buff *skb; - sock_hold(sk); - lock_sock(sk); x25_stop_heartbeat(sk); x25_stop_timer(sk); @@ -362,6 +401,7 @@ void x25_destroy_socket(struct sock *sk) /* * Queue the unaccepted socket for death */ + skb->sk->sk_state = TCP_LISTEN; sock_set_flag(skb->sk, SOCK_DEAD); x25_start_heartbeat(skb->sk); x25_sk(skb->sk)->state = X25_STATE_0; @@ -370,8 +410,7 @@ void x25_destroy_socket(struct sock *sk) kfree_skb(skb); } - if (atomic_read(&sk->sk_wmem_alloc) || - atomic_read(&sk->sk_rmem_alloc)) { + if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ sk->sk_timer.expires = jiffies + 10 * HZ; sk->sk_timer.function = x25_destroy_timer; @@ -381,8 +420,14 @@ void x25_destroy_socket(struct sock *sk) /* drop last reference so sock_put will free */ __sock_put(sk); } +} - release_sock(sk); +void x25_destroy_socket_from_timer(struct sock *sk) +{ + sock_hold(sk); + bh_lock_sock(sk); + __x25_destroy_socket(sk); + bh_unlock_sock(sk); sock_put(sk); } @@ -392,7 +437,7 @@ void x25_destroy_socket(struct sock *sk) */ static int x25_setsockopt(struct socket *sock, int level, int optname, - char __user *optval, int optlen) + char __user *optval, unsigned int optlen) { int opt; struct sock *sk = sock->sk; @@ -409,7 +454,10 @@ static int x25_setsockopt(struct socket *sock, int level, int optname, if (get_user(opt, (int __user *)optval)) goto out; - x25_sk(sk)->qbitincl = !!opt; + if (opt) + set_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); + else + clear_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); rc = 0; out: return rc; @@ -420,7 +468,7 @@ static int x25_getsockopt(struct socket *sock, int level, int optname, { struct sock *sk = sock->sk; int val, len, rc = -ENOPROTOOPT; - + if (level != SOL_X25 || optname != X25_QBITINCL) goto out; @@ -433,12 +481,12 @@ static int x25_getsockopt(struct socket *sock, int level, int optname, rc = -EINVAL; if (len < 0) goto out; - + rc = -EFAULT; if (put_user(len, optlen)) goto out; - val = x25_sk(sk)->qbitincl; + val = test_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags); rc = copy_to_user(optval, &val, len) ? -EFAULT : 0; out: return rc; @@ -449,12 +497,14 @@ static int x25_listen(struct socket *sock, int backlog) struct sock *sk = sock->sk; int rc = -EOPNOTSUPP; + lock_sock(sk); if (sk->sk_state != TCP_LISTEN) { memset(&x25_sk(sk)->dest_addr, 0, X25_ADDR_LEN); sk->sk_max_ack_backlog = backlog; sk->sk_state = TCP_LISTEN; rc = 0; } + release_sock(sk); return rc; } @@ -465,10 +515,10 @@ static struct proto x25_proto = { .obj_size = sizeof(struct x25_sock), }; -static struct sock *x25_alloc_socket(void) +static struct sock *x25_alloc_socket(struct net *net) { struct x25_sock *x25; - struct sock *sk = sk_alloc(AF_X25, GFP_ATOMIC, &x25_proto, 1); + struct sock *sk = sk_alloc(net, AF_X25, GFP_ATOMIC, &x25_proto); if (!sk) goto out; @@ -484,19 +534,26 @@ out: return sk; } -void x25_init_timers(struct sock *sk); - -static int x25_create(struct socket *sock, int protocol) +static int x25_create(struct net *net, struct socket *sock, int protocol, + int kern) { struct sock *sk; struct x25_sock *x25; - int rc = -ESOCKTNOSUPPORT; + int rc = -EAFNOSUPPORT; + + if (!net_eq(net, &init_net)) + goto out; + + rc = -ESOCKTNOSUPPORT; + if (sock->type != SOCK_SEQPACKET) + goto out; - if (sock->type != SOCK_SEQPACKET || protocol) + rc = -EINVAL; + if (protocol) goto out; - rc = -ENOMEM; - if ((sk = x25_alloc_socket()) == NULL) + rc = -ENOBUFS; + if ((sk = x25_alloc_socket(net)) == NULL) goto out; x25 = x25_sk(sk); @@ -515,21 +572,22 @@ static int x25_create(struct socket *sock, int protocol) x25->t2 = sysctl_x25_ack_holdback_timeout; x25->state = X25_STATE_0; x25->cudmatchlength = 0; - x25->accptapprv = X25_DENY_ACCPT_APPRV; /* normally no cud */ + set_bit(X25_ACCPT_APPRV_FLAG, &x25->flags); /* normally no cud */ /* on call accept */ x25->facilities.winsize_in = X25_DEFAULT_WINDOW_SIZE; x25->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE; x25->facilities.pacsize_in = X25_DEFAULT_PACKET_SIZE; x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE; - x25->facilities.throughput = X25_DEFAULT_THROUGHPUT; + x25->facilities.throughput = 0; /* by default don't negotiate + throughput */ x25->facilities.reverse = X25_DEFAULT_REVERSE; - x25->dte_facilities.calling_len = 0; - x25->dte_facilities.called_len = 0; - memset(x25->dte_facilities.called_ae, '\0', - sizeof(x25->dte_facilities.called_ae)); - memset(x25->dte_facilities.calling_ae, '\0', - sizeof(x25->dte_facilities.calling_ae)); + x25->dte_facilities.calling_len = 0; + x25->dte_facilities.called_len = 0; + memset(x25->dte_facilities.called_ae, '\0', + sizeof(x25->dte_facilities.called_ae)); + memset(x25->dte_facilities.calling_ae, '\0', + sizeof(x25->dte_facilities.calling_ae)); rc = 0; out: @@ -544,19 +602,17 @@ static struct sock *x25_make_new(struct sock *osk) if (osk->sk_type != SOCK_SEQPACKET) goto out; - if ((sk = x25_alloc_socket()) == NULL) + if ((sk = x25_alloc_socket(sock_net(osk))) == NULL) goto out; x25 = x25_sk(sk); sk->sk_type = osk->sk_type; - sk->sk_socket = osk->sk_socket; sk->sk_priority = osk->sk_priority; sk->sk_protocol = osk->sk_protocol; sk->sk_rcvbuf = osk->sk_rcvbuf; sk->sk_sndbuf = osk->sk_sndbuf; sk->sk_state = TCP_ESTABLISHED; - sk->sk_sleep = osk->sk_sleep; sk->sk_backlog_rcv = osk->sk_backlog_rcv; sock_copy_flags(sk, osk); @@ -565,12 +621,12 @@ static struct sock *x25_make_new(struct sock *osk) x25->t22 = ox25->t22; x25->t23 = ox25->t23; x25->t2 = ox25->t2; + x25->flags = ox25->flags; x25->facilities = ox25->facilities; - x25->qbitincl = ox25->qbitincl; x25->dte_facilities = ox25->dte_facilities; x25->cudmatchlength = ox25->cudmatchlength; - x25->accptapprv = ox25->accptapprv; + clear_bit(X25_INTERRUPT_FLAG, &x25->flags); x25_init_timers(sk); out: return sk; @@ -582,16 +638,18 @@ static int x25_release(struct socket *sock) struct x25_sock *x25; if (!sk) - goto out; + return 0; x25 = x25_sk(sk); + sock_hold(sk); + lock_sock(sk); switch (x25->state) { case X25_STATE_0: case X25_STATE_2: x25_disconnect(sk, 0, 0, 0); - x25_destroy_socket(sk); + __x25_destroy_socket(sk); goto out; case X25_STATE_1: @@ -609,9 +667,10 @@ static int x25_release(struct socket *sock) break; } - sock->sk = NULL; - sk->sk_socket = NULL; /* Not used, but we should do this */ + sock_orphan(sk); out: + release_sock(sk); + sock_put(sk); return 0; } @@ -619,26 +678,39 @@ static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; + int len, i, rc = 0; if (!sock_flag(sk, SOCK_ZAPPED) || addr_len != sizeof(struct sockaddr_x25) || - addr->sx25_family != AF_X25) - return -EINVAL; + addr->sx25_family != AF_X25) { + rc = -EINVAL; + goto out; + } + + len = strlen(addr->sx25_addr.x25_addr); + for (i = 0; i < len; i++) { + if (!isdigit(addr->sx25_addr.x25_addr[i])) { + rc = -EINVAL; + goto out; + } + } + lock_sock(sk); x25_sk(sk)->source_addr = addr->sx25_addr; x25_insert_socket(sk); sock_reset_flag(sk, SOCK_ZAPPED); + release_sock(sk); SOCK_DEBUG(sk, "x25_bind: socket is bound\n"); - - return 0; +out: + return rc; } static int x25_wait_for_connection_establishment(struct sock *sk) { DECLARE_WAITQUEUE(wait, current); - int rc; + int rc; - add_wait_queue_exclusive(sk->sk_sleep, &wait); + add_wait_queue_exclusive(sk_sleep(sk), &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); rc = -ERESTARTSYS; @@ -658,7 +730,7 @@ static int x25_wait_for_connection_establishment(struct sock *sk) break; } __set_current_state(TASK_RUNNING); - remove_wait_queue(sk->sk_sleep, &wait); + remove_wait_queue(sk_sleep(sk), &wait); return rc; } @@ -687,7 +759,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, if (sk->sk_state == TCP_ESTABLISHED) goto out; - sk->sk_state = TCP_CLOSE; + sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; rc = -EINVAL; @@ -756,7 +828,7 @@ static int x25_wait_for_data(struct sock *sk, long timeout) DECLARE_WAITQUEUE(wait, current); int rc = 0; - add_wait_queue_exclusive(sk->sk_sleep, &wait); + add_wait_queue_exclusive(sk_sleep(sk), &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); if (sk->sk_shutdown & RCV_SHUTDOWN) @@ -776,10 +848,10 @@ static int x25_wait_for_data(struct sock *sk, long timeout) break; } __set_current_state(TASK_RUNNING); - remove_wait_queue(sk->sk_sleep, &wait); + remove_wait_queue(sk_sleep(sk), &wait); return rc; } - + static int x25_accept(struct socket *sock, struct socket *newsock, int flags) { struct sock *sk = sock->sk; @@ -787,7 +859,7 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags) struct sk_buff *skb; int rc = -EINVAL; - if (!sk || sk->sk_state != TCP_LISTEN) + if (!sk) goto out; rc = -EOPNOTSUPP; @@ -795,6 +867,10 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags) goto out; lock_sock(sk); + rc = -EINVAL; + if (sk->sk_state != TCP_LISTEN) + goto out2; + rc = x25_wait_for_data(sk, sk->sk_rcvtimeo); if (rc) goto out2; @@ -803,14 +879,12 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags) if (!skb->sk) goto out2; newsk = skb->sk; - newsk->sk_socket = newsock; - newsk->sk_sleep = &newsock->wait; + sock_graft(newsk, newsock); /* Now attach up the new socket */ skb->sk = NULL; kfree_skb(skb); sk->sk_ack_backlog--; - newsock->sk = newsk; newsock->state = SS_CONNECTED; rc = 0; out2: @@ -825,10 +899,13 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr, struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr; struct sock *sk = sock->sk; struct x25_sock *x25 = x25_sk(sk); + int rc = 0; if (peer) { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; + if (sk->sk_state != TCP_ESTABLISHED) { + rc = -ENOTCONN; + goto out; + } sx25->sx25_addr = x25->dest_addr; } else sx25->sx25_addr = x25->source_addr; @@ -836,9 +913,10 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr, sx25->sx25_family = AF_X25; *uaddr_len = sizeof(*sx25); - return 0; +out: + return rc; } - + int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int lci) { @@ -848,7 +926,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, struct x25_address source_addr, dest_addr; struct x25_facilities facilities; struct x25_dte_facilities dte_facilities; - int len, rc; + int len, addr_len, rc; /* * Remove the LCI and frame type. @@ -858,28 +936,69 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, /* * Extract the X.25 addresses and convert them to ASCII strings, * and remove them. + * + * Address block is mandatory in call request packets */ - skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr)); + addr_len = x25_parse_address_block(skb, &source_addr, &dest_addr); + if (addr_len <= 0) + goto out_clear_request; + skb_pull(skb, addr_len); /* * Get the length of the facilities, skip past them for the moment * get the call user data because this is needed to determine * the correct listener + * + * Facilities length is mandatory in call request packets */ + if (!pskb_may_pull(skb, 1)) + goto out_clear_request; len = skb->data[0] + 1; + if (!pskb_may_pull(skb, len)) + goto out_clear_request; skb_pull(skb,len); /* + * Ensure that the amount of call user data is valid. + */ + if (skb->len > X25_MAX_CUD_LEN) + goto out_clear_request; + + /* + * Get all the call user data so it can be used in + * x25_find_listener and skb_copy_from_linear_data up ahead. + */ + if (!pskb_may_pull(skb, skb->len)) + goto out_clear_request; + + /* * Find a listener for the particular address/cud pair. */ sk = x25_find_listener(&source_addr,skb); skb_push(skb,len); + if (sk != NULL && sk_acceptq_is_full(sk)) { + goto out_sock_put; + } + /* - * We can't accept the Call Request. + * We dont have any listeners for this incoming call. + * Try forwarding it. */ - if (sk == NULL || sk_acceptq_is_full(sk)) - goto out_clear_request; + if (sk == NULL) { + skb_push(skb, addr_len + X25_STD_MIN_LEN); + if (sysctl_x25_forward && + x25_forward_call(&dest_addr, nb, skb, lci) > 0) + { + /* Call was forwarded, dont process it any more */ + kfree_skb(skb); + rc = 1; + goto out; + } else { + /* No listeners, can't forward, clear the call */ + goto out_clear_request; + } + } /* * Try to reach a compromise on the requested facilities. @@ -924,8 +1043,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, makex25->vc_facil_mask &= ~X25_MASK_CALLING_AE; makex25->cudmatchlength = x25_sk(sk)->cudmatchlength; - /* Normally all calls are accepted immediatly */ - if(makex25->accptapprv & X25_DENY_ACCPT_APPRV) { + /* Normally all calls are accepted immediately */ + if (test_bit(X25_ACCPT_APPRV_FLAG, &makex25->flags)) { x25_write_internal(make, X25_CALL_ACCEPTED); makex25->state = X25_STATE_3; } @@ -933,10 +1052,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, /* * Incoming Call User Data. */ - if (skb->len >= 0) { - memcpy(makex25->calluserdata.cuddata, skb->data, skb->len); - makex25->calluserdata.cudlength = skb->len; - } + skb_copy_from_linear_data(skb, makex25->calluserdata.cuddata, skb->len); + makex25->calluserdata.cudlength = skb->len; sk->sk_ack_backlog++; @@ -947,7 +1064,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, x25_start_heartbeat(make); if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk, skb->len); + sk->sk_data_ready(sk); rc = 1; sock_put(sk); out: @@ -965,7 +1082,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, { struct sock *sk = sock->sk; struct x25_sock *x25 = x25_sk(sk); - struct sockaddr_x25 *usx25 = (struct sockaddr_x25 *)msg->msg_name; + DECLARE_SOCKADDR(struct sockaddr_x25 *, usx25, msg->msg_name); struct sockaddr_x25 sx25; struct sk_buff *skb; unsigned char *asmptr; @@ -973,6 +1090,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, size_t size; int qbit = 0, rc = -EINVAL; + lock_sock(sk); if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_OOB|MSG_EOR|MSG_CMSG_COMPAT)) goto out; @@ -1019,6 +1137,12 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, sx25.sx25_addr = x25->dest_addr; } + /* Sanity check the packet size */ + if (len > 65535) { + rc = -EMSGSIZE; + goto out; + } + SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n"); /* Build a packet */ @@ -1029,7 +1153,9 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, size = len + X25_MAX_L2_LEN + X25_EXT_MIN_LEN; + release_sock(sk); skb = sock_alloc_send_skb(sk, size, noblock, &rc); + lock_sock(sk); if (!skb) goto out; X25_SKB_CB(skb)->flags = msg->msg_flags; @@ -1041,9 +1167,10 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, */ SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n"); - asmptr = skb->h.raw = skb_put(skb, len); + skb_reset_transport_header(skb); + skb_put(skb, len); - rc = memcpy_fromiovec(asmptr, msg->msg_iov, len); + rc = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); if (rc) goto out_kfree_skb; @@ -1051,7 +1178,10 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, * If the Q BIT Include socket option is in force, the first * byte of the user data is the logical value of the Q Bit. */ - if (x25->qbitincl) { + if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { + if (!pskb_may_pull(skb, 1)) + goto out_kfree_skb; + qbit = skb->data[0]; skb_pull(skb, 1); } @@ -1103,32 +1233,18 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, if (msg->msg_flags & MSG_OOB) skb_queue_tail(&x25->interrupt_out_queue, skb); else { - len = x25_output(sk, skb); - if (len < 0) + rc = x25_output(sk, skb); + len = rc; + if (rc < 0) kfree_skb(skb); - else if (x25->qbitincl) + else if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) len++; } - /* - * lock_sock() is currently only used to serialize this x25_kick() - * against input-driven x25_kick() calls. It currently only blocks - * incoming packets for this socket and does not protect against - * any other socket state changes and is not called from anywhere - * else. As x25_kick() cannot block and as long as all socket - * operations are BKL-wrapped, we don't need take to care about - * purging the backlog queue in x25_release(). - * - * Using lock_sock() to protect all socket operations entirely - * (and making the whole x25 stack SMP aware) unfortunately would - * require major changes to {send,recv}msg and skb allocation methods. - * -> 2.5 ;) - */ - lock_sock(sk); x25_kick(sk); - release_sock(sk); rc = len; out: + release_sock(sk); return rc; out_kfree_skb: kfree_skb(skb); @@ -1142,13 +1258,21 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, { struct sock *sk = sock->sk; struct x25_sock *x25 = x25_sk(sk); - struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name; + DECLARE_SOCKADDR(struct sockaddr_x25 *, sx25, msg->msg_name); size_t copied; - int qbit; + int qbit, header_len; struct sk_buff *skb; unsigned char *asmptr; int rc = -ENOTCONN; + lock_sock(sk); + + if (x25->neighbour == NULL) + goto out; + + header_len = x25->neighbour->extended ? + X25_EXT_MIN_LEN : X25_STD_MIN_LEN; + /* * This works for seqpacket too. The receiver has ordered the queue for * us! We do one quick check first though @@ -1164,12 +1288,15 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, skb = skb_dequeue(&x25->interrupt_in_queue); + if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) + goto out_free_dgram; + skb_pull(skb, X25_STD_MIN_LEN); /* * No Q bit information on Interrupt data. */ - if (x25->qbitincl) { + if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { asmptr = skb_push(skb, 1); *asmptr = 0x00; } @@ -1177,24 +1304,27 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_flags |= MSG_OOB; } else { /* Now we can treat all alike */ + release_sock(sk); skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &rc); + lock_sock(sk); if (!skb) goto out; + if (!pskb_may_pull(skb, header_len)) + goto out_free_dgram; + qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT; - skb_pull(skb, x25->neighbour->extended ? - X25_EXT_MIN_LEN : X25_STD_MIN_LEN); + skb_pull(skb, header_len); - if (x25->qbitincl) { + if (test_bit(X25_Q_BIT_FLAG, &x25->flags)) { asmptr = skb_push(skb, 1); *asmptr = qbit; } } - skb->h.raw = skb->data; - + skb_reset_transport_header(skb); copied = skb->len; if (copied > size) { @@ -1202,7 +1332,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_flags |= MSG_TRUNC; } - /* Currently, each datagram always contains a complete record */ + /* Currently, each datagram always contains a complete record */ msg->msg_flags |= MSG_EOR; rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); @@ -1212,17 +1342,15 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, if (sx25) { sx25->sx25_family = AF_X25; sx25->sx25_addr = x25->dest_addr; + msg->msg_namelen = sizeof(*sx25); } - msg->msg_namelen = sizeof(struct sockaddr_x25); - - lock_sock(sk); x25_check_rbuf(sk); - release_sock(sk); rc = copied; out_free_dgram: skb_free_datagram(sk, skb); out: + release_sock(sk); return rc; } @@ -1235,206 +1363,260 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) int rc; switch (cmd) { - case TIOCOUTQ: { - int amount = sk->sk_sndbuf - - atomic_read(&sk->sk_wmem_alloc); - if (amount < 0) - amount = 0; - rc = put_user(amount, (unsigned int __user *)argp); - break; - } + case TIOCOUTQ: { + int amount; - case TIOCINQ: { - struct sk_buff *skb; - int amount = 0; - /* - * These two are safe on a single CPU system as - * only user tasks fiddle here - */ - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - rc = put_user(amount, (unsigned int __user *)argp); - break; - } + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + if (amount < 0) + amount = 0; + rc = put_user(amount, (unsigned int __user *)argp); + break; + } - case SIOCGSTAMP: - rc = -EINVAL; - if (sk) - rc = sock_get_timestamp(sk, - (struct timeval __user *)argp); - break; - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - rc = -EINVAL; - break; - case SIOCADDRT: - case SIOCDELRT: - rc = -EPERM; - if (!capable(CAP_NET_ADMIN)) - break; - rc = x25_route_ioctl(cmd, argp); - break; - case SIOCX25GSUBSCRIP: - rc = x25_subscr_ioctl(cmd, argp); + case TIOCINQ: { + struct sk_buff *skb; + int amount = 0; + /* + * These two are safe on a single CPU system as + * only user tasks fiddle here + */ + lock_sock(sk); + if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) + amount = skb->len; + release_sock(sk); + rc = put_user(amount, (unsigned int __user *)argp); + break; + } + + case SIOCGSTAMP: + rc = -EINVAL; + if (sk) + rc = sock_get_timestamp(sk, + (struct timeval __user *)argp); + break; + case SIOCGSTAMPNS: + rc = -EINVAL; + if (sk) + rc = sock_get_timestampns(sk, + (struct timespec __user *)argp); + break; + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + rc = -EINVAL; + break; + case SIOCADDRT: + case SIOCDELRT: + rc = -EPERM; + if (!capable(CAP_NET_ADMIN)) break; - case SIOCX25SSUBSCRIP: - rc = -EPERM; - if (!capable(CAP_NET_ADMIN)) - break; - rc = x25_subscr_ioctl(cmd, argp); + rc = x25_route_ioctl(cmd, argp); + break; + case SIOCX25GSUBSCRIP: + rc = x25_subscr_ioctl(cmd, argp); + break; + case SIOCX25SSUBSCRIP: + rc = -EPERM; + if (!capable(CAP_NET_ADMIN)) break; - case SIOCX25GFACILITIES: { - struct x25_facilities fac = x25->facilities; - rc = copy_to_user(argp, &fac, - sizeof(fac)) ? -EFAULT : 0; + rc = x25_subscr_ioctl(cmd, argp); + break; + case SIOCX25GFACILITIES: { + lock_sock(sk); + rc = copy_to_user(argp, &x25->facilities, + sizeof(x25->facilities)) + ? -EFAULT : 0; + release_sock(sk); + break; + } + + case SIOCX25SFACILITIES: { + struct x25_facilities facilities; + rc = -EFAULT; + if (copy_from_user(&facilities, argp, sizeof(facilities))) break; + rc = -EINVAL; + lock_sock(sk); + if (sk->sk_state != TCP_LISTEN && + sk->sk_state != TCP_CLOSE) + goto out_fac_release; + if (facilities.pacsize_in < X25_PS16 || + facilities.pacsize_in > X25_PS4096) + goto out_fac_release; + if (facilities.pacsize_out < X25_PS16 || + facilities.pacsize_out > X25_PS4096) + goto out_fac_release; + if (facilities.winsize_in < 1 || + facilities.winsize_in > 127) + goto out_fac_release; + if (facilities.throughput) { + int out = facilities.throughput & 0xf0; + int in = facilities.throughput & 0x0f; + if (!out) + facilities.throughput |= + X25_DEFAULT_THROUGHPUT << 4; + else if (out < 0x30 || out > 0xD0) + goto out_fac_release; + if (!in) + facilities.throughput |= + X25_DEFAULT_THROUGHPUT; + else if (in < 0x03 || in > 0x0D) + goto out_fac_release; } + if (facilities.reverse && + (facilities.reverse & 0x81) != 0x81) + goto out_fac_release; + x25->facilities = facilities; + rc = 0; +out_fac_release: + release_sock(sk); + break; + } - case SIOCX25SFACILITIES: { - struct x25_facilities facilities; + case SIOCX25GDTEFACILITIES: { + lock_sock(sk); + rc = copy_to_user(argp, &x25->dte_facilities, + sizeof(x25->dte_facilities)); + release_sock(sk); + if (rc) rc = -EFAULT; - if (copy_from_user(&facilities, argp, - sizeof(facilities))) - break; - rc = -EINVAL; - if (sk->sk_state != TCP_LISTEN && - sk->sk_state != TCP_CLOSE) - break; - if (facilities.pacsize_in < X25_PS16 || - facilities.pacsize_in > X25_PS4096) - break; - if (facilities.pacsize_out < X25_PS16 || - facilities.pacsize_out > X25_PS4096) - break; - if (facilities.winsize_in < 1 || - facilities.winsize_in > 127) - break; - if (facilities.throughput < 0x03 || - facilities.throughput > 0xDD) - break; - if (facilities.reverse && - (facilities.reverse | 0x81)!= 0x81) - break; - x25->facilities = facilities; - rc = 0; - break; - } + break; + } - case SIOCX25GDTEFACILITIES: { - rc = copy_to_user(argp, &x25->dte_facilities, - sizeof(x25->dte_facilities)); - if (rc) - rc = -EFAULT; - break; - } - - case SIOCX25SDTEFACILITIES: { - struct x25_dte_facilities dtefacs; - rc = -EFAULT; - if (copy_from_user(&dtefacs, argp, sizeof(dtefacs))) - break; - rc = -EINVAL; - if (sk->sk_state != TCP_LISTEN && - sk->sk_state != TCP_CLOSE) - break; - if (dtefacs.calling_len > X25_MAX_AE_LEN) - break; - if (dtefacs.calling_ae == NULL) - break; - if (dtefacs.called_len > X25_MAX_AE_LEN) - break; - if (dtefacs.called_ae == NULL) - break; - x25->dte_facilities = dtefacs; - rc = 0; + case SIOCX25SDTEFACILITIES: { + struct x25_dte_facilities dtefacs; + rc = -EFAULT; + if (copy_from_user(&dtefacs, argp, sizeof(dtefacs))) break; - } + rc = -EINVAL; + lock_sock(sk); + if (sk->sk_state != TCP_LISTEN && + sk->sk_state != TCP_CLOSE) + goto out_dtefac_release; + if (dtefacs.calling_len > X25_MAX_AE_LEN) + goto out_dtefac_release; + if (dtefacs.calling_ae == NULL) + goto out_dtefac_release; + if (dtefacs.called_len > X25_MAX_AE_LEN) + goto out_dtefac_release; + if (dtefacs.called_ae == NULL) + goto out_dtefac_release; + x25->dte_facilities = dtefacs; + rc = 0; +out_dtefac_release: + release_sock(sk); + break; + } - case SIOCX25GCALLUSERDATA: { - struct x25_calluserdata cud = x25->calluserdata; - rc = copy_to_user(argp, &cud, - sizeof(cud)) ? -EFAULT : 0; - break; - } + case SIOCX25GCALLUSERDATA: { + lock_sock(sk); + rc = copy_to_user(argp, &x25->calluserdata, + sizeof(x25->calluserdata)) + ? -EFAULT : 0; + release_sock(sk); + break; + } - case SIOCX25SCALLUSERDATA: { - struct x25_calluserdata calluserdata; + case SIOCX25SCALLUSERDATA: { + struct x25_calluserdata calluserdata; - rc = -EFAULT; - if (copy_from_user(&calluserdata, argp, - sizeof(calluserdata))) - break; - rc = -EINVAL; - if (calluserdata.cudlength > X25_MAX_CUD_LEN) - break; - x25->calluserdata = calluserdata; - rc = 0; + rc = -EFAULT; + if (copy_from_user(&calluserdata, argp, sizeof(calluserdata))) break; - } - - case SIOCX25GCAUSEDIAG: { - struct x25_causediag causediag; - causediag = x25->causediag; - rc = copy_to_user(argp, &causediag, - sizeof(causediag)) ? -EFAULT : 0; + rc = -EINVAL; + if (calluserdata.cudlength > X25_MAX_CUD_LEN) break; - } + lock_sock(sk); + x25->calluserdata = calluserdata; + release_sock(sk); + rc = 0; + break; + } - case SIOCX25SCUDMATCHLEN: { - struct x25_subaddr sub_addr; - rc = -EINVAL; - if(sk->sk_state != TCP_CLOSE) - break; - rc = -EFAULT; - if (copy_from_user(&sub_addr, argp, - sizeof(sub_addr))) - break; - rc = -EINVAL; - if(sub_addr.cudmatchlength > X25_MAX_CUD_LEN) - break; - x25->cudmatchlength = sub_addr.cudmatchlength; - rc = 0; - break; - } + case SIOCX25GCAUSEDIAG: { + lock_sock(sk); + rc = copy_to_user(argp, &x25->causediag, sizeof(x25->causediag)) + ? -EFAULT : 0; + release_sock(sk); + break; + } - case SIOCX25CALLACCPTAPPRV: { - rc = -EINVAL; - if (sk->sk_state != TCP_CLOSE) - break; - x25->accptapprv = X25_ALLOW_ACCPT_APPRV; - rc = 0; + case SIOCX25SCAUSEDIAG: { + struct x25_causediag causediag; + rc = -EFAULT; + if (copy_from_user(&causediag, argp, sizeof(causediag))) break; - } + lock_sock(sk); + x25->causediag = causediag; + release_sock(sk); + rc = 0; + break; - case SIOCX25SENDCALLACCPT: { - rc = -EINVAL; - if (sk->sk_state != TCP_ESTABLISHED) - break; - if (x25->accptapprv) /* must call accptapprv above */ - break; - x25_write_internal(sk, X25_CALL_ACCEPTED); - x25->state = X25_STATE_3; + } + + case SIOCX25SCUDMATCHLEN: { + struct x25_subaddr sub_addr; + rc = -EINVAL; + lock_sock(sk); + if(sk->sk_state != TCP_CLOSE) + goto out_cud_release; + rc = -EFAULT; + if (copy_from_user(&sub_addr, argp, + sizeof(sub_addr))) + goto out_cud_release; + rc = -EINVAL; + if (sub_addr.cudmatchlength > X25_MAX_CUD_LEN) + goto out_cud_release; + x25->cudmatchlength = sub_addr.cudmatchlength; + rc = 0; +out_cud_release: + release_sock(sk); + break; + } + + case SIOCX25CALLACCPTAPPRV: { + rc = -EINVAL; + lock_sock(sk); + if (sk->sk_state == TCP_CLOSE) { + clear_bit(X25_ACCPT_APPRV_FLAG, &x25->flags); rc = 0; - break; } + release_sock(sk); + break; + } - default: - rc = -ENOIOCTLCMD; - break; + case SIOCX25SENDCALLACCPT: { + rc = -EINVAL; + lock_sock(sk); + if (sk->sk_state != TCP_ESTABLISHED) + goto out_sendcallaccpt_release; + /* must call accptapprv above */ + if (test_bit(X25_ACCPT_APPRV_FLAG, &x25->flags)) + goto out_sendcallaccpt_release; + x25_write_internal(sk, X25_CALL_ACCEPTED); + x25->state = X25_STATE_3; + rc = 0; +out_sendcallaccpt_release: + release_sock(sk); + break; + } + + default: + rc = -ENOIOCTLCMD; + break; } return rc; } -static struct net_proto_family x25_family_ops = { +static const struct net_proto_family x25_family_ops = { .family = AF_X25, .create = x25_create, .owner = THIS_MODULE, @@ -1465,16 +1647,20 @@ static int compat_x25_subscr_ioctl(unsigned int cmd, dev_put(dev); if (cmd == SIOCX25GSUBSCRIP) { + read_lock_bh(&x25_neigh_list_lock); x25_subscr.extended = nb->extended; x25_subscr.global_facil_mask = nb->global_facil_mask; + read_unlock_bh(&x25_neigh_list_lock); rc = copy_to_user(x25_subscr32, &x25_subscr, sizeof(*x25_subscr32)) ? -EFAULT : 0; } else { rc = -EINVAL; if (x25_subscr.extended == 0 || x25_subscr.extended == 1) { rc = 0; + write_lock_bh(&x25_neigh_list_lock); nb->extended = x25_subscr.extended; nb->global_facil_mask = x25_subscr.global_facil_mask; + write_unlock_bh(&x25_neigh_list_lock); } } x25_neigh_put(nb); @@ -1504,6 +1690,12 @@ static int compat_x25_ioctl(struct socket *sock, unsigned int cmd, rc = compat_sock_get_timestamp(sk, (struct timeval __user*)argp); break; + case SIOCGSTAMPNS: + rc = -EINVAL; + if (sk) + rc = compat_sock_get_timestampns(sk, + (struct timespec __user*)argp); + break; case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: @@ -1539,6 +1731,7 @@ static int compat_x25_ioctl(struct socket *sock, unsigned int cmd, case SIOCX25GCALLUSERDATA: case SIOCX25SCALLUSERDATA: case SIOCX25GCAUSEDIAG: + case SIOCX25SCAUSEDIAG: case SIOCX25SCUDMATCHLEN: case SIOCX25CALLACCPTAPPRV: case SIOCX25SENDCALLACCPT: @@ -1552,7 +1745,7 @@ static int compat_x25_ioctl(struct socket *sock, unsigned int cmd, } #endif -static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = { +static const struct proto_ops x25_proto_ops = { .family = AF_X25, .owner = THIS_MODULE, .release = x25_release, @@ -1576,11 +1769,8 @@ static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = { .sendpage = sock_no_sendpage, }; -#include <linux/smp_lock.h> -SOCKOPS_WRAP(x25_proto, AF_X25); - -static struct packet_type x25_packet_type = { - .type = __constant_htons(ETH_P_X25), +static struct packet_type x25_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_X25), .func = x25_lapb_receive_frame, }; @@ -1591,15 +1781,17 @@ static struct notifier_block x25_dev_notifier = { void x25_kill_by_neigh(struct x25_neigh *nb) { struct sock *s; - struct hlist_node *node; write_lock_bh(&x25_list_lock); - sk_for_each(s, node, &x25_list) + sk_for_each(s, &x25_list) if (x25_sk(s)->neighbour == nb) x25_disconnect(s, ENETUNREACH, 0, 0); write_unlock_bh(&x25_list_lock); + + /* Remove any related forwards */ + x25_clear_forward_by_dev(nb->dev); } static int __init x25_init(void) @@ -1609,20 +1801,31 @@ static int __init x25_init(void) if (rc != 0) goto out; - sock_register(&x25_family_ops); + rc = sock_register(&x25_family_ops); + if (rc != 0) + goto out_proto; dev_add_pack(&x25_packet_type); - register_netdevice_notifier(&x25_dev_notifier); + rc = register_netdevice_notifier(&x25_dev_notifier); + if (rc != 0) + goto out_sock; - printk(KERN_INFO "X.25 for Linux. Version 0.2 for Linux 2.1.15\n"); + pr_info("Linux Version 0.2\n"); -#ifdef CONFIG_SYSCTL x25_register_sysctl(); -#endif - x25_proc_init(); + rc = x25_proc_init(); + if (rc != 0) + goto out_dev; out: return rc; +out_dev: + unregister_netdevice_notifier(&x25_dev_notifier); +out_sock: + sock_unregister(AF_X25); +out_proto: + proto_unregister(&x25_proto); + goto out; } module_init(x25_init); @@ -1632,9 +1835,7 @@ static void __exit x25_exit(void) x25_link_free(); x25_route_free(); -#ifdef CONFIG_SYSCTL x25_unregister_sysctl(); -#endif unregister_netdevice_notifier(&x25_dev_notifier); |
