diff options
Diffstat (limited to 'net/x25')
| -rw-r--r-- | net/x25/Kconfig | 7 | ||||
| -rw-r--r-- | net/x25/Makefile | 2 | ||||
| -rw-r--r-- | net/x25/af_x25.c | 959 | ||||
| -rw-r--r-- | net/x25/sysctl_net_x25.c | 57 | ||||
| -rw-r--r-- | net/x25/x25_dev.c | 129 | ||||
| -rw-r--r-- | net/x25/x25_facilities.c | 163 | ||||
| -rw-r--r-- | net/x25/x25_forward.c | 170 | ||||
| -rw-r--r-- | net/x25/x25_in.c | 180 | ||||
| -rw-r--r-- | net/x25/x25_link.c | 108 | ||||
| -rw-r--r-- | net/x25/x25_out.c | 29 | ||||
| -rw-r--r-- | net/x25/x25_proc.c | 186 | ||||
| -rw-r--r-- | net/x25/x25_route.c | 21 | ||||
| -rw-r--r-- | net/x25/x25_subr.c | 111 | ||||
| -rw-r--r-- | net/x25/x25_timer.c | 16 |
14 files changed, 1433 insertions, 705 deletions
diff --git a/net/x25/Kconfig b/net/x25/Kconfig index e6759c9660b..e2fa133f9fb 100644 --- a/net/x25/Kconfig +++ b/net/x25/Kconfig @@ -3,8 +3,7 @@ # config X25 - tristate "CCITT X.25 Packet Layer (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "CCITT X.25 Packet Layer" ---help--- X.25 is a set of standardized network protocols, similar in scope to frame relay; the one physical line from your box to the X.25 network @@ -17,8 +16,8 @@ config X25 if you want that) and the lower level data link layer protocol LAPB (say Y to "LAPB Data Link Driver" below if you want that). - You can read more about X.25 at <http://www.sangoma.com/x25.htm> and - <http://www.cisco.com/univercd/cc/td/doc/product/software/ios11/cbook/cx25.htm>. + You can read more about X.25 at <http://www.sangoma.com/tutorials/x25/> and + <http://docwiki.cisco.com/wiki/X.25>. Information about X.25 for Linux is contained in the files <file:Documentation/networking/x25.txt> and <file:Documentation/networking/x25-iface.txt>. diff --git a/net/x25/Makefile b/net/x25/Makefile index 587a71aa411..a2c34ab6f19 100644 --- a/net/x25/Makefile +++ b/net/x25/Makefile @@ -6,5 +6,5 @@ obj-$(CONFIG_X25) += x25.o x25-y := af_x25.o x25_dev.o x25_facilities.o x25_in.o \ x25_link.o x25_out.o x25_route.o x25_subr.o \ - x25_timer.o x25_proc.o + x25_timer.o x25_proc.o x25_forward.o x25-$(CONFIG_SYSCTL) += sysctl_net_x25.o diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 72b6ff3299b..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,7 +35,8 @@ * response */ -#include <linux/config.h> +#define pr_fmt(fmt) "X25: " fmt + #include <linux/module.h> #include <linux/capability.h> #include <linux/errno.h> @@ -47,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,13 +56,18 @@ #include <linux/termios.h> /* For TIOCINQ/OUTQ */ #include <linux/notifier.h> #include <linux/init.h> +#include <linux/compat.h> +#include <linux/ctype.h> + #include <net/x25.h> +#include <net/compat.h> int sysctl_x25_restart_request_timeout = X25_DEFAULT_T20; 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); @@ -69,12 +76,55 @@ static const struct proto_ops x25_proto_ops; static struct x25_address null_x25_address = {" "}; +#ifdef CONFIG_COMPAT +struct compat_x25_subscrip_struct { + char device[200-sizeof(compat_ulong_t)]; + compat_ulong_t global_facil_mask; + compat_uint_t extended; +}; +#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; @@ -160,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); @@ -177,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; } } @@ -229,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, @@ -244,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; @@ -271,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; @@ -318,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); } /* @@ -335,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); @@ -352,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; @@ -360,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; @@ -371,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); } @@ -382,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; @@ -399,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; @@ -410,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; @@ -423,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; @@ -439,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; } @@ -455,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; @@ -474,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 (sock->type != SOCK_SEQPACKET || protocol) + if (!net_eq(net, &init_net)) goto out; - rc = -ENOMEM; - if ((sk = x25_alloc_socket()) == NULL) + rc = -ESOCKTNOSUPPORT; + if (sock->type != SOCK_SEQPACKET) + goto out; + + rc = -EINVAL; + if (protocol) + goto out; + + rc = -ENOBUFS; + if ((sk = x25_alloc_socket(net)) == NULL) goto out; x25 = x25_sk(sk); @@ -505,15 +572,23 @@ 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)); + rc = 0; out: return rc; @@ -527,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); @@ -548,11 +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; @@ -564,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: @@ -591,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; } @@ -601,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; @@ -640,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; } @@ -669,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; @@ -733,12 +823,12 @@ out: return rc; } -static int x25_wait_for_data(struct sock *sk, int timeout) +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) @@ -758,10 +848,10 @@ static int x25_wait_for_data(struct sock *sk, int 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; @@ -769,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; @@ -777,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; @@ -785,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: @@ -807,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; @@ -818,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) { @@ -829,7 +925,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, struct x25_sock *makex25; struct x25_address source_addr, dest_addr; struct x25_facilities facilities; - int len, rc; + struct x25_dte_facilities dte_facilities; + int len, addr_len, rc; /* * Remove the LCI and frame type. @@ -839,33 +936,75 @@ 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. */ - if ((len = x25_negotiate_facilities(skb, sk, &facilities)) == -1) + len = x25_negotiate_facilities(skb, sk, &facilities, &dte_facilities); + if (len == -1) goto out_sock_put; /* @@ -896,13 +1035,16 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, makex25->source_addr = source_addr; makex25->neighbour = nb; makex25->facilities = facilities; + makex25->dte_facilities= dte_facilities; makex25->vc_facil_mask = x25_sk(sk)->vc_facil_mask; /* ensure no reverse facil on accept */ makex25->vc_facil_mask &= ~X25_MASK_REVERSE; + /* ensure no calling address extension on accept */ + 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; } @@ -910,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++; @@ -924,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: @@ -942,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; @@ -950,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; @@ -996,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 */ @@ -1006,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; @@ -1018,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; @@ -1028,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); } @@ -1080,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); @@ -1119,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 @@ -1141,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; } @@ -1154,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) { @@ -1179,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); @@ -1189,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; } @@ -1212,182 +1363,389 @@ 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; + + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + if (amount < 0) + amount = 0; + rc = put_user(amount, (unsigned int __user *)argp); + break; + } - 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; - } + 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 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 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 SIOCX25GCALLUSERDATA: { - struct x25_calluserdata cud = x25->calluserdata; - rc = copy_to_user(argp, &cud, - sizeof(cud)) ? -EFAULT : 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 SIOCX25SCALLUSERDATA: { - struct x25_calluserdata calluserdata; + case SIOCX25GCALLUSERDATA: { + lock_sock(sk); + rc = copy_to_user(argp, &x25->calluserdata, + sizeof(x25->calluserdata)) + ? -EFAULT : 0; + release_sock(sk); + break; + } - 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; - break; - } + case SIOCX25SCALLUSERDATA: { + struct x25_calluserdata calluserdata; - case SIOCX25GCAUSEDIAG: { - struct x25_causediag causediag; - causediag = x25->causediag; - rc = copy_to_user(argp, &causediag, - sizeof(causediag)) ? -EFAULT : 0; + rc = -EFAULT; + if (copy_from_user(&calluserdata, argp, sizeof(calluserdata))) 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; + rc = -EINVAL; + if (calluserdata.cudlength > X25_MAX_CUD_LEN) break; - } + lock_sock(sk); + x25->calluserdata = calluserdata; + release_sock(sk); + rc = 0; + break; + } - case SIOCX25CALLACCPTAPPRV: { - rc = -EINVAL; - if (sk->sk_state != TCP_CLOSE) - break; - x25->accptapprv = X25_ALLOW_ACCPT_APPRV; - rc = 0; + case SIOCX25GCAUSEDIAG: { + lock_sock(sk); + rc = copy_to_user(argp, &x25->causediag, sizeof(x25->causediag)) + ? -EFAULT : 0; + release_sock(sk); + break; + } + + 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, }; -static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = { +#ifdef CONFIG_COMPAT +static int compat_x25_subscr_ioctl(unsigned int cmd, + struct compat_x25_subscrip_struct __user *x25_subscr32) +{ + struct compat_x25_subscrip_struct x25_subscr; + struct x25_neigh *nb; + struct net_device *dev; + int rc = -EINVAL; + + rc = -EFAULT; + if (copy_from_user(&x25_subscr, x25_subscr32, sizeof(*x25_subscr32))) + goto out; + + rc = -EINVAL; + dev = x25_dev_get(x25_subscr.device); + if (dev == NULL) + goto out; + + nb = x25_get_neigh(dev); + if (nb == NULL) + goto out_dev_put; + + 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); +out: + return rc; +out_dev_put: + dev_put(dev); + goto out; +} + +static int compat_x25_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = compat_ptr(arg); + struct sock *sk = sock->sk; + + int rc = -ENOIOCTLCMD; + + switch(cmd) { + case TIOCOUTQ: + case TIOCINQ: + rc = x25_ioctl(sock, cmd, (unsigned long)argp); + break; + case SIOCGSTAMP: + rc = -EINVAL; + if (sk) + 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: + 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 = compat_x25_subscr_ioctl(cmd, argp); + break; + case SIOCX25SSUBSCRIP: + rc = -EPERM; + if (!capable(CAP_NET_ADMIN)) + break; + rc = compat_x25_subscr_ioctl(cmd, argp); + break; + case SIOCX25GFACILITIES: + case SIOCX25SFACILITIES: + case SIOCX25GDTEFACILITIES: + case SIOCX25SDTEFACILITIES: + case SIOCX25GCALLUSERDATA: + case SIOCX25SCALLUSERDATA: + case SIOCX25GCAUSEDIAG: + case SIOCX25SCAUSEDIAG: + case SIOCX25SCUDMATCHLEN: + case SIOCX25CALLACCPTAPPRV: + case SIOCX25SENDCALLACCPT: + rc = x25_ioctl(sock, cmd, (unsigned long)argp); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + return rc; +} +#endif + +static const struct proto_ops x25_proto_ops = { .family = AF_X25, .owner = THIS_MODULE, .release = x25_release, @@ -1398,6 +1756,9 @@ static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = { .getname = x25_getname, .poll = datagram_poll, .ioctl = x25_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_x25_ioctl, +#endif .listen = x25_listen, .shutdown = sock_no_shutdown, .setsockopt = x25_setsockopt, @@ -1408,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, }; @@ -1423,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) @@ -1441,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); @@ -1464,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); diff --git a/net/x25/sysctl_net_x25.c b/net/x25/sysctl_net_x25.c index aabda59c824..43239527a20 100644 --- a/net/x25/sysctl_net_x25.c +++ b/net/x25/sysctl_net_x25.c @@ -18,90 +18,67 @@ static int max_timer[] = { 300 * HZ }; static struct ctl_table_header *x25_table_header; static struct ctl_table x25_table[] = { - { - .ctl_name = NET_X25_RESTART_REQUEST_TIMEOUT, + { .procname = "restart_request_timeout", .data = &sysctl_x25_restart_request_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &min_timer, .extra2 = &max_timer, }, - { - .ctl_name = NET_X25_CALL_REQUEST_TIMEOUT, + { .procname = "call_request_timeout", .data = &sysctl_x25_call_request_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &min_timer, .extra2 = &max_timer, }, - { - .ctl_name = NET_X25_RESET_REQUEST_TIMEOUT, + { .procname = "reset_request_timeout", .data = &sysctl_x25_reset_request_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &min_timer, .extra2 = &max_timer, }, - { - .ctl_name = NET_X25_CLEAR_REQUEST_TIMEOUT, + { .procname = "clear_request_timeout", .data = &sysctl_x25_clear_request_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &min_timer, .extra2 = &max_timer, }, - { - .ctl_name = NET_X25_ACK_HOLD_BACK_TIMEOUT, + { .procname = "acknowledgement_hold_back_timeout", .data = &sysctl_x25_ack_holdback_timeout, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, + .proc_handler = proc_dointvec_minmax, .extra1 = &min_timer, .extra2 = &max_timer, }, - { 0, }, -}; - -static struct ctl_table x25_dir_table[] = { - { - .ctl_name = NET_X25, - .procname = "x25", - .mode = 0555, - .child = x25_table, - }, - { 0, }, -}; - -static struct ctl_table x25_root_table[] = { { - .ctl_name = CTL_NET, - .procname = "net", - .mode = 0555, - .child = x25_dir_table, + .procname = "x25_forward", + .data = &sysctl_x25_forward, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, }, { 0, }, }; void __init x25_register_sysctl(void) { - x25_table_header = register_sysctl_table(x25_root_table, 1); + x25_table_header = register_net_sysctl(&init_net, "net/x25", x25_table); } void x25_unregister_sysctl(void) { - unregister_sysctl_table(x25_table_header); + unregister_net_sysctl_table(x25_table_header); } diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index adfe7b8df35..39231237e1c 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -1,8 +1,8 @@ /* * X.25 Packet Layer release 002 * - * 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. + * 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. * * This code REQUIRES 2.1.15 or higher * @@ -17,13 +17,16 @@ * 2000-09-04 Henner Eisen Prevent freeing a dangling skb. */ -#include <linux/config.h> +#define pr_fmt(fmt) "X25: " fmt + #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/skbuff.h> +#include <linux/slab.h> #include <net/sock.h> #include <linux/if_arp.h> #include <net/x25.h> +#include <net/x25device.h> static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb) { @@ -31,8 +34,11 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb) unsigned short frametype; unsigned int lci; + if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) + return 0; + frametype = skb->data[2]; - lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); + lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); /* * LCI of zero is always for us, and its always a link control @@ -49,14 +55,15 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb) if ((sk = x25_find_socket(lci, nb)) != NULL) { int queued = 1; - skb->h.raw = skb->data; + skb_reset_transport_header(skb); bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { queued = x25_process_rx_frame(sk, skb); } else { - sk_add_backlog(sk, skb); + queued = !sk_add_backlog(sk, skb, sk->sk_rcvbuf); } bh_unlock_sock(sk); + sock_put(sk); return queued; } @@ -67,15 +74,24 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb) return x25_rx_call_request(skb, nb, lci); /* - * Its not a Call Request, nor is it a control frame. - * Let caller throw it away. + * Its not a Call Request, nor is it a control frame. + * Can we forward it? */ + + if (x25_forward_data(lci, nb, skb)) { + if (frametype == X25_CLEAR_CONFIRMATION) { + x25_clear_forward_by_lci(lci); + } + kfree_skb(skb); + return 1; + } + /* x25_transmit_clear_request(nb, lci, 0x0D); */ if (frametype != X25_CLEAR_CONFIRMATION) - printk(KERN_DEBUG "x25_receive_data(): unknown frame type %2x\n",frametype); + pr_debug("x25_receive_data(): unknown frame type %2x\n",frametype); return 0; } @@ -86,6 +102,9 @@ int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev, struct sk_buff *nskb; struct x25_neigh *nb; + if (!net_eq(dev_net(dev), &init_net)) + goto drop; + nskb = skb_copy(skb, GFP_ATOMIC); if (!nskb) goto drop; @@ -97,24 +116,30 @@ int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev, */ nb = x25_get_neigh(dev); if (!nb) { - printk(KERN_DEBUG "X.25: unknown neighbour - %s\n", dev->name); + pr_debug("unknown neighbour - %s\n", dev->name); goto drop; } + if (!pskb_may_pull(skb, 1)) + return 0; + switch (skb->data[0]) { - case 0x00: - skb_pull(skb, 1); - if (x25_receive_data(skb, nb)) { - x25_neigh_put(nb); - goto out; - } - break; - case 0x01: - x25_link_established(nb); - break; - case 0x02: - x25_link_terminated(nb); - break; + + case X25_IFACE_DATA: + skb_pull(skb, 1); + if (x25_receive_data(skb, nb)) { + x25_neigh_put(nb); + goto out; + } + break; + + case X25_IFACE_CONNECT: + x25_link_established(nb); + break; + + case X25_IFACE_DISCONNECT: + x25_link_terminated(nb); + break; } x25_neigh_put(nb); drop: @@ -129,21 +154,21 @@ void x25_establish_link(struct x25_neigh *nb) unsigned char *ptr; switch (nb->dev->type) { - case ARPHRD_X25: - if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) { - printk(KERN_ERR "x25_dev: out of memory\n"); - return; - } - ptr = skb_put(skb, 1); - *ptr = 0x01; - break; - -#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) - case ARPHRD_ETHER: + case ARPHRD_X25: + if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) { + pr_err("x25_dev: out of memory\n"); return; + } + ptr = skb_put(skb, 1); + *ptr = X25_IFACE_CONNECT; + break; + +#if IS_ENABLED(CONFIG_LLC) + case ARPHRD_ETHER: + return; #endif - default: - return; + default: + return; } skb->protocol = htons(ETH_P_X25); @@ -157,7 +182,7 @@ void x25_terminate_link(struct x25_neigh *nb) struct sk_buff *skb; unsigned char *ptr; -#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) +#if IS_ENABLED(CONFIG_LLC) if (nb->dev->type == ARPHRD_ETHER) return; #endif @@ -166,12 +191,12 @@ void x25_terminate_link(struct x25_neigh *nb) skb = alloc_skb(1, GFP_ATOMIC); if (!skb) { - printk(KERN_ERR "x25_dev: out of memory\n"); + pr_err("x25_dev: out of memory\n"); return; } ptr = skb_put(skb, 1); - *ptr = 0x02; + *ptr = X25_IFACE_DISCONNECT; skb->protocol = htons(ETH_P_X25); skb->dev = nb->dev; @@ -182,22 +207,22 @@ void x25_send_frame(struct sk_buff *skb, struct x25_neigh *nb) { unsigned char *dptr; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); switch (nb->dev->type) { - case ARPHRD_X25: - dptr = skb_push(skb, 1); - *dptr = 0x00; - break; - -#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) - case ARPHRD_ETHER: - kfree_skb(skb); - return; + case ARPHRD_X25: + dptr = skb_push(skb, 1); + *dptr = X25_IFACE_DATA; + break; + +#if IS_ENABLED(CONFIG_LLC) + case ARPHRD_ETHER: + kfree_skb(skb); + return; #endif - default: - kfree_skb(skb); - return; + default: + kfree_skb(skb); + return; } skb->protocol = htons(ETH_P_X25); diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c index 54278b962f4..7ecd04c2136 100644 --- a/net/x25/x25_facilities.c +++ b/net/x25/x25_facilities.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 * @@ -15,34 +15,68 @@ * * History * X.25 001 Split from x25_subr.c - * mar/20/00 Daniela Squassoni Disabling/enabling of facilities + * mar/20/00 Daniela Squassoni Disabling/enabling of facilities * negotiation. * apr/14/05 Shaun Pereira - Allow fast select with no restriction * on response. */ +#define pr_fmt(fmt) "X25: " fmt + #include <linux/kernel.h> #include <linux/string.h> #include <linux/skbuff.h> #include <net/sock.h> #include <net/x25.h> -/* - * Parse a set of facilities into the facilities structure. Unrecognised - * facilities are written to the debug log file. +/** + * x25_parse_facilities - Parse facilities from skb into the facilities structs + * + * @skb: sk_buff to parse + * @facilities: Regular facilities, updated as facilities are found + * @dte_facs: ITU DTE facilities, updated as DTE facilities are found + * @vc_fac_mask: mask is updated with all facilities found + * + * Return codes: + * -1 - Parsing error, caller should drop call and clean up + * 0 - Parse OK, this skb has no facilities + * >0 - Parse OK, returns the length of the facilities header + * */ -int x25_parse_facilities(struct sk_buff *skb, - struct x25_facilities *facilities, - unsigned long *vc_fac_mask) +int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities, + struct x25_dte_facilities *dte_facs, unsigned long *vc_fac_mask) { - unsigned char *p = skb->data; - unsigned int len = *p++; + unsigned char *p; + unsigned int len; *vc_fac_mask = 0; + /* + * The kernel knows which facilities were set on an incoming call but + * currently this information is not available to userspace. Here we + * give userspace who read incoming call facilities 0 length to indicate + * it wasn't set. + */ + dte_facs->calling_len = 0; + dte_facs->called_len = 0; + memset(dte_facs->called_ae, '\0', sizeof(dte_facs->called_ae)); + memset(dte_facs->calling_ae, '\0', sizeof(dte_facs->calling_ae)); + + if (!pskb_may_pull(skb, 1)) + return 0; + + len = skb->data[0]; + + if (!pskb_may_pull(skb, 1 + len)) + return -1; + + p = skb->data + 1; + while (len > 0) { switch (*p & X25_FAC_CLASS_MASK) { case X25_FAC_CLASS_A: + if (len < 2) + return -1; switch (*p) { case X25_FAC_REVERSE: if((p[1] & 0x81) == 0x81) { @@ -74,8 +108,10 @@ int x25_parse_facilities(struct sk_buff *skb, facilities->throughput = p[1]; *vc_fac_mask |= X25_MASK_THROUGHPUT; break; + case X25_MARKER: + break; default: - printk(KERN_DEBUG "X.25: unknown facility " + pr_debug("unknown facility " "%02X, value %02X\n", p[0], p[1]); break; @@ -84,6 +120,8 @@ int x25_parse_facilities(struct sk_buff *skb, len -= 2; break; case X25_FAC_CLASS_B: + if (len < 3) + return -1; switch (*p) { case X25_FAC_PACKET_SIZE: facilities->pacsize_in = p[1]; @@ -96,7 +134,7 @@ int x25_parse_facilities(struct sk_buff *skb, *vc_fac_mask |= X25_MASK_WINDOW_SIZE; break; default: - printk(KERN_DEBUG "X.25: unknown facility " + pr_debug("unknown facility " "%02X, values %02X, %02X\n", p[0], p[1], p[2]); break; @@ -105,18 +143,43 @@ int x25_parse_facilities(struct sk_buff *skb, len -= 3; break; case X25_FAC_CLASS_C: - printk(KERN_DEBUG "X.25: unknown facility %02X, " + if (len < 4) + return -1; + pr_debug("unknown facility %02X, " "values %02X, %02X, %02X\n", p[0], p[1], p[2], p[3]); p += 4; len -= 4; break; case X25_FAC_CLASS_D: - printk(KERN_DEBUG "X.25: unknown facility %02X, " - "length %d, values %02X, %02X, %02X, %02X\n", - p[0], p[1], p[2], p[3], p[4], p[5]); + if (len < p[1] + 2) + return -1; + switch (*p) { + case X25_FAC_CALLING_AE: + if (p[1] > X25_MAX_DTE_FACIL_LEN || p[1] <= 1) + return -1; + if (p[2] > X25_MAX_AE_LEN) + return -1; + dte_facs->calling_len = p[2]; + memcpy(dte_facs->calling_ae, &p[3], p[1] - 1); + *vc_fac_mask |= X25_MASK_CALLING_AE; + break; + case X25_FAC_CALLED_AE: + if (p[1] > X25_MAX_DTE_FACIL_LEN || p[1] <= 1) + return -1; + if (p[2] > X25_MAX_AE_LEN) + return -1; + dte_facs->called_len = p[2]; + memcpy(dte_facs->called_ae, &p[3], p[1] - 1); + *vc_fac_mask |= X25_MASK_CALLED_AE; + break; + default: + pr_debug("unknown facility %02X," + "length %d\n", p[0], p[1]); + break; + } len -= p[1] + 2; - p += p[1] + 2; + p += p[1] + 2; break; } } @@ -128,8 +191,8 @@ int x25_parse_facilities(struct sk_buff *skb, * Create a set of facilities. */ int x25_create_facilities(unsigned char *buffer, - struct x25_facilities *facilities, - unsigned long facil_mask) + struct x25_facilities *facilities, + struct x25_dte_facilities *dte_facs, unsigned long facil_mask) { unsigned char *p = buffer + 1; int len; @@ -168,6 +231,31 @@ int x25_create_facilities(unsigned char *buffer, *p++ = facilities->winsize_out ? : facilities->winsize_in; } + if (facil_mask & (X25_MASK_CALLING_AE|X25_MASK_CALLED_AE)) { + *p++ = X25_MARKER; + *p++ = X25_DTE_SERVICES; + } + + if (dte_facs->calling_len && (facil_mask & X25_MASK_CALLING_AE)) { + unsigned int bytecount = (dte_facs->calling_len + 1) >> 1; + *p++ = X25_FAC_CALLING_AE; + *p++ = 1 + bytecount; + *p++ = dte_facs->calling_len; + memcpy(p, dte_facs->calling_ae, bytecount); + p += bytecount; + } + + if (dte_facs->called_len && (facil_mask & X25_MASK_CALLED_AE)) { + unsigned int bytecount = (dte_facs->called_len % 2) ? + dte_facs->called_len / 2 + 1 : + dte_facs->called_len / 2; + *p++ = X25_FAC_CALLED_AE; + *p++ = 1 + bytecount; + *p++ = dte_facs->called_len; + memcpy(p, dte_facs->called_ae, bytecount); + p+=bytecount; + } + len = p - buffer; buffer[0] = len - 1; @@ -180,7 +268,7 @@ int x25_create_facilities(unsigned char *buffer, * The only real problem is with reverse charging. */ int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, - struct x25_facilities *new) + struct x25_facilities *new, struct x25_dte_facilities *dte) { struct x25_sock *x25 = x25_sk(sk); struct x25_facilities *ours = &x25->facilities; @@ -190,43 +278,54 @@ int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, memset(&theirs, 0, sizeof(theirs)); memcpy(new, ours, sizeof(*new)); - len = x25_parse_facilities(skb, &theirs, &x25->vc_facil_mask); + len = x25_parse_facilities(skb, &theirs, dte, &x25->vc_facil_mask); + if (len < 0) + return len; /* * They want reverse charging, we won't accept it. */ if ((theirs.reverse & 0x01 ) && (ours->reverse & 0x01)) { - SOCK_DEBUG(sk, "X.25: rejecting reverse charging request"); + SOCK_DEBUG(sk, "X.25: rejecting reverse charging request\n"); return -1; } new->reverse = theirs.reverse; if (theirs.throughput) { - if (theirs.throughput < ours->throughput) { - SOCK_DEBUG(sk, "X.25: throughput negotiated down"); - new->throughput = theirs.throughput; + int theirs_in = theirs.throughput & 0x0f; + int theirs_out = theirs.throughput & 0xf0; + int ours_in = ours->throughput & 0x0f; + int ours_out = ours->throughput & 0xf0; + if (!ours_in || theirs_in < ours_in) { + SOCK_DEBUG(sk, "X.25: inbound throughput negotiated\n"); + new->throughput = (new->throughput & 0xf0) | theirs_in; + } + if (!ours_out || theirs_out < ours_out) { + SOCK_DEBUG(sk, + "X.25: outbound throughput negotiated\n"); + new->throughput = (new->throughput & 0x0f) | theirs_out; } } if (theirs.pacsize_in && theirs.pacsize_out) { if (theirs.pacsize_in < ours->pacsize_in) { - SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down"); + SOCK_DEBUG(sk, "X.25: packet size inwards negotiated down\n"); new->pacsize_in = theirs.pacsize_in; } if (theirs.pacsize_out < ours->pacsize_out) { - SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down"); + SOCK_DEBUG(sk, "X.25: packet size outwards negotiated down\n"); new->pacsize_out = theirs.pacsize_out; } } if (theirs.winsize_in && theirs.winsize_out) { if (theirs.winsize_in < ours->winsize_in) { - SOCK_DEBUG(sk, "X.25: window size inwards negotiated down"); + SOCK_DEBUG(sk, "X.25: window size inwards negotiated down\n"); new->winsize_in = theirs.winsize_in; } if (theirs.winsize_out < ours->winsize_out) { - SOCK_DEBUG(sk, "X.25: window size outwards negotiated down"); + SOCK_DEBUG(sk, "X.25: window size outwards negotiated down\n"); new->winsize_out = theirs.winsize_out; } } @@ -235,7 +334,7 @@ int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, } /* - * Limit values of certain facilities according to the capability of the + * Limit values of certain facilities according to the capability of the * currently attached x25 link. */ void x25_limit_facilities(struct x25_facilities *facilities, @@ -244,12 +343,12 @@ void x25_limit_facilities(struct x25_facilities *facilities, if (!nb->extended) { if (facilities->winsize_in > 7) { - printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n"); + pr_debug("incoming winsize limited to 7\n"); facilities->winsize_in = 7; } if (facilities->winsize_out > 7) { facilities->winsize_out = 7; - printk( KERN_DEBUG "X.25: outgoing winsize limited to 7\n"); + pr_debug("outgoing winsize limited to 7\n"); } } } diff --git a/net/x25/x25_forward.c b/net/x25/x25_forward.c new file mode 100644 index 00000000000..cf561f1613e --- /dev/null +++ b/net/x25/x25_forward.c @@ -0,0 +1,170 @@ +/* + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * History + * 03-01-2007 Added forwarding for x.25 Andrew Hendry + */ + +#define pr_fmt(fmt) "X25: " fmt + +#include <linux/if_arp.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <net/x25.h> + +LIST_HEAD(x25_forward_list); +DEFINE_RWLOCK(x25_forward_list_lock); + +int x25_forward_call(struct x25_address *dest_addr, struct x25_neigh *from, + struct sk_buff *skb, int lci) +{ + struct x25_route *rt; + struct x25_neigh *neigh_new = NULL; + struct list_head *entry; + struct x25_forward *x25_frwd, *new_frwd; + struct sk_buff *skbn; + short same_lci = 0; + int rc = 0; + + if ((rt = x25_get_route(dest_addr)) == NULL) + goto out_no_route; + + if ((neigh_new = x25_get_neigh(rt->dev)) == NULL) { + /* This shouldn't happen, if it occurs somehow + * do something sensible + */ + goto out_put_route; + } + + /* Avoid a loop. This is the normal exit path for a + * system with only one x.25 iface and default route + */ + if (rt->dev == from->dev) { + goto out_put_nb; + } + + /* Remote end sending a call request on an already + * established LCI? It shouldn't happen, just in case.. + */ + read_lock_bh(&x25_forward_list_lock); + list_for_each(entry, &x25_forward_list) { + x25_frwd = list_entry(entry, struct x25_forward, node); + if (x25_frwd->lci == lci) { + pr_warn("call request for lci which is already registered!, transmitting but not registering new pair\n"); + same_lci = 1; + } + } + read_unlock_bh(&x25_forward_list_lock); + + /* Save the forwarding details for future traffic */ + if (!same_lci){ + if ((new_frwd = kmalloc(sizeof(struct x25_forward), + GFP_ATOMIC)) == NULL){ + rc = -ENOMEM; + goto out_put_nb; + } + new_frwd->lci = lci; + new_frwd->dev1 = rt->dev; + new_frwd->dev2 = from->dev; + write_lock_bh(&x25_forward_list_lock); + list_add(&new_frwd->node, &x25_forward_list); + write_unlock_bh(&x25_forward_list_lock); + } + + /* Forward the call request */ + if ( (skbn = skb_clone(skb, GFP_ATOMIC)) == NULL){ + goto out_put_nb; + } + x25_transmit_link(skbn, neigh_new); + rc = 1; + + +out_put_nb: + x25_neigh_put(neigh_new); + +out_put_route: + x25_route_put(rt); + +out_no_route: + return rc; +} + + +int x25_forward_data(int lci, struct x25_neigh *from, struct sk_buff *skb) { + + struct x25_forward *frwd; + struct list_head *entry; + struct net_device *peer = NULL; + struct x25_neigh *nb; + struct sk_buff *skbn; + int rc = 0; + + read_lock_bh(&x25_forward_list_lock); + list_for_each(entry, &x25_forward_list) { + frwd = list_entry(entry, struct x25_forward, node); + if (frwd->lci == lci) { + /* The call is established, either side can send */ + if (from->dev == frwd->dev1) { + peer = frwd->dev2; + } else { + peer = frwd->dev1; + } + break; + } + } + read_unlock_bh(&x25_forward_list_lock); + + if ( (nb = x25_get_neigh(peer)) == NULL) + goto out; + + if ( (skbn = pskb_copy(skb, GFP_ATOMIC)) == NULL){ + goto output; + + } + x25_transmit_link(skbn, nb); + + rc = 1; +output: + x25_neigh_put(nb); +out: + return rc; +} + +void x25_clear_forward_by_lci(unsigned int lci) +{ + struct x25_forward *fwd; + struct list_head *entry, *tmp; + + write_lock_bh(&x25_forward_list_lock); + + list_for_each_safe(entry, tmp, &x25_forward_list) { + fwd = list_entry(entry, struct x25_forward, node); + if (fwd->lci == lci) { + list_del(&fwd->node); + kfree(fwd); + } + } + write_unlock_bh(&x25_forward_list_lock); +} + + +void x25_clear_forward_by_dev(struct net_device *dev) +{ + struct x25_forward *fwd; + struct list_head *entry, *tmp; + + write_lock_bh(&x25_forward_list_lock); + + list_for_each_safe(entry, tmp, &x25_forward_list) { + fwd = list_entry(entry, struct x25_forward, node); + if ((fwd->dev1 == dev) || (fwd->dev2 == dev)){ + list_del(&fwd->node); + kfree(fwd); + } + } + write_unlock_bh(&x25_forward_list_lock); +} diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index 26146874b83..7ac50098a37 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.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 * @@ -17,12 +17,15 @@ * X.25 001 Jonathan Naylor Started coding. * X.25 002 Jonathan Naylor Centralised disconnection code. * New timer architecture. - * 2000-03-20 Daniela Squassoni Disabling/enabling of facilities + * 2000-03-20 Daniela Squassoni Disabling/enabling of facilities * negotiation. * 2000-11-10 Henner Eisen Check and reset for out-of-sequence * i-frames. */ +#define pr_fmt(fmt) "X25: " fmt + +#include <linux/slab.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/string.h> @@ -53,27 +56,30 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) skb_queue_tail(&x25->fragment_queue, skb); - skbn->h.raw = skbn->data; + skb_reset_transport_header(skbn); skbo = skb_dequeue(&x25->fragment_queue); - memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + skb_copy_from_linear_data(skbo, skb_put(skbn, skbo->len), + skbo->len); kfree_skb(skbo); while ((skbo = skb_dequeue(&x25->fragment_queue)) != NULL) { skb_pull(skbo, (x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN); - memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + skb_copy_from_linear_data(skbo, + skb_put(skbn, skbo->len), + skbo->len); kfree_skb(skbo); } - x25->fraglen = 0; + x25->fraglen = 0; } skb_set_owner_r(skbn, sk); skb_queue_tail(&sk->sk_receive_queue, skbn); if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_data_ready(sk, skbn->len); + sk->sk_data_ready(sk); return 0; } @@ -86,49 +92,75 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) { struct x25_address source_addr, dest_addr; + int len; + struct x25_sock *x25 = x25_sk(sk); switch (frametype) { - case X25_CALL_ACCEPTED: { - struct x25_sock *x25 = x25_sk(sk); - - x25_stop_timer(sk); - x25->condition = 0x00; - x25->vs = 0; - x25->va = 0; - x25->vr = 0; - x25->vl = 0; - x25->state = X25_STATE_3; - sk->sk_state = TCP_ESTABLISHED; - /* - * Parse the data in the frame. - */ - skb_pull(skb, X25_STD_MIN_LEN); - skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr)); - skb_pull(skb, - x25_parse_facilities(skb, &x25->facilities, - &x25->vc_facil_mask)); - /* - * Copy any Call User Data. - */ - if (skb->len >= 0) { - memcpy(x25->calluserdata.cuddata, skb->data, - skb->len); - x25->calluserdata.cudlength = skb->len; - } - if (!sock_flag(sk, SOCK_DEAD)) - sk->sk_state_change(sk); - break; + case X25_CALL_ACCEPTED: { + + x25_stop_timer(sk); + x25->condition = 0x00; + x25->vs = 0; + x25->va = 0; + x25->vr = 0; + x25->vl = 0; + x25->state = X25_STATE_3; + sk->sk_state = TCP_ESTABLISHED; + /* + * Parse the data in the frame. + */ + if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) + goto out_clear; + skb_pull(skb, X25_STD_MIN_LEN); + + len = x25_parse_address_block(skb, &source_addr, + &dest_addr); + if (len > 0) + skb_pull(skb, len); + else if (len < 0) + goto out_clear; + + len = x25_parse_facilities(skb, &x25->facilities, + &x25->dte_facilities, + &x25->vc_facil_mask); + if (len > 0) + skb_pull(skb, len); + else if (len < 0) + goto out_clear; + /* + * Copy any Call User Data. + */ + if (skb->len > 0) { + if (skb->len > X25_MAX_CUD_LEN) + goto out_clear; + + skb_copy_bits(skb, 0, x25->calluserdata.cuddata, + skb->len); + x25->calluserdata.cudlength = skb->len; } - case X25_CLEAR_REQUEST: - x25_write_internal(sk, X25_CLEAR_CONFIRMATION); - x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); - break; + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_state_change(sk); + break; + } + case X25_CLEAR_REQUEST: + if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) + goto out_clear; - default: - break; + x25_write_internal(sk, X25_CLEAR_CONFIRMATION); + x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); + break; + + default: + break; } return 0; + +out_clear: + x25_write_internal(sk, X25_CLEAR_REQUEST); + x25->state = X25_STATE_2; + x25_start_t23timer(sk); + return 0; } /* @@ -141,6 +173,9 @@ static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametyp switch (frametype) { case X25_CLEAR_REQUEST: + if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) + goto out_clear; + x25_write_internal(sk, X25_CLEAR_CONFIRMATION); x25_disconnect(sk, 0, skb->data[3], skb->data[4]); break; @@ -154,6 +189,11 @@ static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametyp } return 0; + +out_clear: + x25_write_internal(sk, X25_CLEAR_REQUEST); + x25_start_t23timer(sk); + return 0; } /* @@ -166,7 +206,7 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp int queued = 0; int modulus; struct x25_sock *x25 = x25_sk(sk); - + modulus = (x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; switch (frametype) { @@ -183,6 +223,9 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_CLEAR_REQUEST: + if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) + goto out_clear; + x25_write_internal(sk, X25_CLEAR_CONFIRMATION); x25_disconnect(sk, 0, skb->data[3], skb->data[4]); break; @@ -242,7 +285,7 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; } if (atomic_read(&sk->sk_rmem_alloc) > - (sk->sk_rcvbuf / 2)) + (sk->sk_rcvbuf >> 1)) x25->condition |= X25_COND_OWN_RX_BUSY; } /* @@ -260,7 +303,7 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_INTERRUPT_CONFIRMATION: - x25->intflag = 0; + clear_bit(X25_INTERRUPT_FLAG, &x25->flags); break; case X25_INTERRUPT: @@ -276,11 +319,17 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; default: - printk(KERN_WARNING "x25: unknown %02X in state 3\n", frametype); + pr_warn("unknown %02X in state 3\n", frametype); break; } return queued; + +out_clear: + x25_write_internal(sk, X25_CLEAR_REQUEST); + x25->state = X25_STATE_2; + x25_start_t23timer(sk); + return 0; } /* @@ -290,13 +339,13 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp */ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + struct x25_sock *x25 = x25_sk(sk); + switch (frametype) { case X25_RESET_REQUEST: x25_write_internal(sk, X25_RESET_CONFIRMATION); case X25_RESET_CONFIRMATION: { - struct x25_sock *x25 = x25_sk(sk); - x25_stop_timer(sk); x25->condition = 0x00; x25->va = 0; @@ -308,6 +357,9 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; } case X25_CLEAR_REQUEST: + if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) + goto out_clear; + x25_write_internal(sk, X25_CLEAR_CONFIRMATION); x25_disconnect(sk, 0, skb->data[3], skb->data[4]); break; @@ -317,6 +369,12 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp } return 0; + +out_clear: + x25_write_internal(sk, X25_CLEAR_REQUEST); + x25->state = X25_STATE_2; + x25_start_t23timer(sk); + return 0; } /* Higher level upcall for a LAPB frame */ @@ -331,18 +389,18 @@ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m); switch (x25->state) { - case X25_STATE_1: - queued = x25_state1_machine(sk, skb, frametype); - break; - case X25_STATE_2: - queued = x25_state2_machine(sk, skb, frametype); - break; - case X25_STATE_3: - queued = x25_state3_machine(sk, skb, frametype, ns, nr, q, d, m); - break; - case X25_STATE_4: - queued = x25_state4_machine(sk, skb, frametype); - break; + case X25_STATE_1: + queued = x25_state1_machine(sk, skb, frametype); + break; + case X25_STATE_2: + queued = x25_state2_machine(sk, skb, frametype); + break; + case X25_STATE_3: + queued = x25_state3_machine(sk, skb, frametype, ns, nr, q, d, m); + break; + case X25_STATE_4: + queued = x25_state4_machine(sk, skb, frametype); + break; } x25_kick(sk); diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index 0a760fe6684..fd5ffb25873 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.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 * @@ -16,22 +16,25 @@ * History * X.25 001 Jonathan Naylor Started coding. * X.25 002 Jonathan Naylor New timer architecture. - * mar/20/00 Daniela Squassoni Disabling/enabling of facilities + * mar/20/00 Daniela Squassoni Disabling/enabling of facilities * negotiation. * 2000-09-04 Henner Eisen dev_hold() / dev_put() for x25_neigh. */ +#define pr_fmt(fmt) "X25: " fmt + #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/timer.h> +#include <linux/slab.h> #include <linux/netdevice.h> #include <linux/skbuff.h> #include <asm/uaccess.h> #include <linux/init.h> #include <net/x25.h> -static struct list_head x25_neigh_list = LIST_HEAD_INIT(x25_neigh_list); -static DEFINE_RWLOCK(x25_neigh_list_lock); +LIST_HEAD(x25_neigh_list); +DEFINE_RWLOCK(x25_neigh_list_lock); static void x25_t20timer_expiry(unsigned long); @@ -75,30 +78,32 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb, int confirm; switch (frametype) { - case X25_RESTART_REQUEST: - confirm = !x25_t20timer_pending(nb); - x25_stop_t20timer(nb); - nb->state = X25_LINK_STATE_3; - if (confirm) - x25_transmit_restart_confirmation(nb); + case X25_RESTART_REQUEST: + confirm = !x25_t20timer_pending(nb); + x25_stop_t20timer(nb); + nb->state = X25_LINK_STATE_3; + if (confirm) + x25_transmit_restart_confirmation(nb); + break; + + case X25_RESTART_CONFIRMATION: + x25_stop_t20timer(nb); + nb->state = X25_LINK_STATE_3; + break; + + case X25_DIAGNOSTIC: + if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 4)) break; - case X25_RESTART_CONFIRMATION: - x25_stop_t20timer(nb); - nb->state = X25_LINK_STATE_3; - break; + pr_warn("diagnostic #%d - %02X %02X %02X\n", + skb->data[3], skb->data[4], + skb->data[5], skb->data[6]); + break; - case X25_DIAGNOSTIC: - printk(KERN_WARNING "x25: diagnostic #%d - " - "%02X %02X %02X\n", - skb->data[3], skb->data[4], - skb->data[5], skb->data[6]); - break; - - default: - printk(KERN_WARNING "x25: received unknown %02X " - "with LCI 000\n", frametype); - break; + default: + pr_warn("received unknown %02X with LCI 000\n", + frametype); + break; } if (nb->state == X25_LINK_STATE_3) @@ -192,18 +197,18 @@ void x25_transmit_clear_request(struct x25_neigh *nb, unsigned int lci, void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *nb) { switch (nb->state) { - case X25_LINK_STATE_0: - skb_queue_tail(&nb->queue, skb); - nb->state = X25_LINK_STATE_1; - x25_establish_link(nb); - break; - case X25_LINK_STATE_1: - case X25_LINK_STATE_2: - skb_queue_tail(&nb->queue, skb); - break; - case X25_LINK_STATE_3: - x25_send_frame(skb, nb); - break; + case X25_LINK_STATE_0: + skb_queue_tail(&nb->queue, skb); + nb->state = X25_LINK_STATE_1; + x25_establish_link(nb); + break; + case X25_LINK_STATE_1: + case X25_LINK_STATE_2: + skb_queue_tail(&nb->queue, skb); + break; + case X25_LINK_STATE_3: + x25_send_frame(skb, nb); + break; } } @@ -213,14 +218,14 @@ void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *nb) void x25_link_established(struct x25_neigh *nb) { switch (nb->state) { - case X25_LINK_STATE_0: - nb->state = X25_LINK_STATE_2; - break; - case X25_LINK_STATE_1: - x25_transmit_restart_request(nb); - nb->state = X25_LINK_STATE_2; - x25_start_t20timer(nb); - break; + case X25_LINK_STATE_0: + nb->state = X25_LINK_STATE_2; + break; + case X25_LINK_STATE_1: + x25_transmit_restart_request(nb); + nb->state = X25_LINK_STATE_2; + x25_start_t20timer(nb); + break; } } @@ -247,10 +252,7 @@ void x25_link_device_up(struct net_device *dev) return; skb_queue_head_init(&nb->queue); - - init_timer(&nb->t20timer); - nb->t20timer.data = (unsigned long)nb; - nb->t20timer.function = &x25_t20timer_expiry; + setup_timer(&nb->t20timer, x25_t20timer_expiry, (unsigned long)nb); dev_hold(dev); nb->dev = dev; @@ -362,16 +364,20 @@ int x25_subscr_ioctl(unsigned int cmd, void __user *arg) 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(arg, &x25_subscr, sizeof(x25_subscr)) ? -EFAULT : 0; } else { rc = -EINVAL; if (!(x25_subscr.extended && 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); @@ -394,8 +400,12 @@ void __exit x25_link_free(void) write_lock_bh(&x25_neigh_list_lock); list_for_each_safe(entry, tmp, &x25_neigh_list) { + struct net_device *dev; + nb = list_entry(entry, struct x25_neigh, node); + dev = nb->dev; __x25_remove_neigh(nb); + dev_put(dev); } write_unlock_bh(&x25_neigh_list_lock); } diff --git a/net/x25/x25_out.c b/net/x25/x25_out.c index a2e62cea819..0144271d218 100644 --- a/net/x25/x25_out.c +++ b/net/x25/x25_out.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 * @@ -22,6 +22,7 @@ * needed cleaned seq-number fields. */ +#include <linux/slab.h> #include <linux/socket.h> #include <linux/kernel.h> #include <linux/string.h> @@ -61,14 +62,17 @@ int x25_output(struct sock *sk, struct sk_buff *skb) if (skb->len - header_len > max_len) { /* Save a copy of the Header */ - memcpy(header, skb->data, header_len); + skb_copy_from_linear_data(skb, header, header_len); skb_pull(skb, header_len); frontlen = skb_headroom(skb); while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, - noblock, &err)) == NULL){ + release_sock(sk); + skbn = sock_alloc_send_skb(sk, frontlen + max_len, + noblock, &err); + lock_sock(sk); + if (!skbn) { if (err == -EWOULDBLOCK && noblock){ kfree_skb(skb); return sent; @@ -78,18 +82,18 @@ int x25_output(struct sock *sk, struct sk_buff *skb) "sent\n", err, sent); return err; } - + skb_reserve(skbn, frontlen); len = max_len > skb->len ? skb->len : max_len; /* Copy the user data */ - memcpy(skb_put(skbn, len), skb->data, len); + skb_copy_from_linear_data(skb, skb_put(skbn, len), len); skb_pull(skb, len); /* Duplicate the Header */ skb_push(skbn, header_len); - memcpy(skbn->data, header, header_len); + skb_copy_to_linear_data(skbn, header, header_len); if (skb->len > 0) { if (x25->neighbour->extended) @@ -101,7 +105,7 @@ int x25_output(struct sock *sk, struct sk_buff *skb) skb_queue_tail(&sk->sk_write_queue, skbn); sent += len; } - + kfree_skb(skb); } else { skb_queue_tail(&sk->sk_write_queue, skb); @@ -110,7 +114,7 @@ int x25_output(struct sock *sk, struct sk_buff *skb) return sent; } -/* +/* * This procedure is passed a buffer descriptor for an iframe. It builds * the rest of the control part of the frame and then writes it out. */ @@ -131,7 +135,7 @@ static void x25_send_iframe(struct sock *sk, struct sk_buff *skb) skb->data[2] |= (x25->vr << 5) & 0xE0; } - x25_transmit_link(skb, x25->neighbour); + x25_transmit_link(skb, x25->neighbour); } void x25_kick(struct sock *sk) @@ -147,8 +151,9 @@ void x25_kick(struct sock *sk) /* * Transmit interrupt data. */ - if (!x25->intflag && skb_peek(&x25->interrupt_out_queue) != NULL) { - x25->intflag = 1; + if (skb_peek(&x25->interrupt_out_queue) != NULL && + !test_and_set_bit(X25_INTERRUPT_FLAG, &x25->flags)) { + skb = skb_dequeue(&x25->interrupt_out_queue); x25_transmit_link(skb, x25->neighbour); } diff --git a/net/x25/x25_proc.c b/net/x25/x25_proc.c index dfb80116c59..0917f047f2c 100644 --- a/net/x25/x25_proc.c +++ b/net/x25/x25_proc.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.4 with seq_file support * @@ -17,68 +17,39 @@ * 2002/10/06 Arnaldo Carvalho de Melo seq_file support */ -#include <linux/config.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/export.h> +#include <net/net_namespace.h> #include <net/sock.h> #include <net/x25.h> #ifdef CONFIG_PROC_FS -static __inline__ struct x25_route *x25_get_route_idx(loff_t pos) -{ - struct list_head *route_entry; - struct x25_route *rt = NULL; - - list_for_each(route_entry, &x25_route_list) { - rt = list_entry(route_entry, struct x25_route, node); - if (!pos--) - goto found; - } - rt = NULL; -found: - return rt; -} static void *x25_seq_route_start(struct seq_file *seq, loff_t *pos) + __acquires(x25_route_list_lock) { - loff_t l = *pos; - read_lock_bh(&x25_route_list_lock); - return l ? x25_get_route_idx(--l) : SEQ_START_TOKEN; + return seq_list_start_head(&x25_route_list, *pos); } static void *x25_seq_route_next(struct seq_file *seq, void *v, loff_t *pos) { - struct x25_route *rt; - - ++*pos; - if (v == SEQ_START_TOKEN) { - rt = NULL; - if (!list_empty(&x25_route_list)) - rt = list_entry(x25_route_list.next, - struct x25_route, node); - goto out; - } - rt = v; - if (rt->node.next != &x25_route_list) - rt = list_entry(rt->node.next, struct x25_route, node); - else - rt = NULL; -out: - return rt; + return seq_list_next(v, &x25_route_list, pos); } static void x25_seq_route_stop(struct seq_file *seq, void *v) + __releases(x25_route_list_lock) { read_unlock_bh(&x25_route_list_lock); } static int x25_seq_route_show(struct seq_file *seq, void *v) { - struct x25_route *rt; + struct x25_route *rt = list_entry(v, struct x25_route, node); - if (v == SEQ_START_TOKEN) { + if (v == &x25_route_list) { seq_puts(seq, "Address Digits Device\n"); goto out; } @@ -89,44 +60,22 @@ static int x25_seq_route_show(struct seq_file *seq, void *v) rt->dev ? rt->dev->name : "???"); out: return 0; -} - -static __inline__ struct sock *x25_get_socket_idx(loff_t pos) -{ - struct sock *s; - struct hlist_node *node; - - sk_for_each(s, node, &x25_list) - if (!pos--) - goto found; - s = NULL; -found: - return s; } static void *x25_seq_socket_start(struct seq_file *seq, loff_t *pos) + __acquires(x25_list_lock) { - loff_t l = *pos; - read_lock_bh(&x25_list_lock); - return l ? x25_get_socket_idx(--l) : SEQ_START_TOKEN; + return seq_hlist_start_head(&x25_list, *pos); } static void *x25_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos) { - struct sock *s; - - ++*pos; - if (v == SEQ_START_TOKEN) { - s = sk_head(&x25_list); - goto out; - } - s = sk_next(v); -out: - return s; + return seq_hlist_next(v, &x25_list, pos); } static void x25_seq_socket_stop(struct seq_file *seq, void *v) + __releases(x25_list_lock) { read_unlock_bh(&x25_list_lock); } @@ -144,7 +93,7 @@ static int x25_seq_socket_show(struct seq_file *seq, void *v) goto out; } - s = v; + s = sk_entry(v); x25 = x25_sk(s); if (!x25->neighbour || (dev = x25->neighbour->dev) == NULL) @@ -159,27 +108,69 @@ static int x25_seq_socket_show(struct seq_file *seq, void *v) devname, x25->lci & 0x0FFF, x25->state, x25->vs, x25->vr, x25->va, x25_display_timer(s) / HZ, x25->t2 / HZ, x25->t21 / HZ, x25->t22 / HZ, x25->t23 / HZ, - atomic_read(&s->sk_wmem_alloc), - atomic_read(&s->sk_rmem_alloc), + sk_wmem_alloc_get(s), + sk_rmem_alloc_get(s), s->sk_socket ? SOCK_INODE(s->sk_socket)->i_ino : 0L); out: return 0; -} +} -static struct seq_operations x25_seq_route_ops = { +static void *x25_seq_forward_start(struct seq_file *seq, loff_t *pos) + __acquires(x25_forward_list_lock) +{ + read_lock_bh(&x25_forward_list_lock); + return seq_list_start_head(&x25_forward_list, *pos); +} + +static void *x25_seq_forward_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return seq_list_next(v, &x25_forward_list, pos); +} + +static void x25_seq_forward_stop(struct seq_file *seq, void *v) + __releases(x25_forward_list_lock) +{ + read_unlock_bh(&x25_forward_list_lock); +} + +static int x25_seq_forward_show(struct seq_file *seq, void *v) +{ + struct x25_forward *f = list_entry(v, struct x25_forward, node); + + if (v == &x25_forward_list) { + seq_printf(seq, "lci dev1 dev2\n"); + goto out; + } + + f = v; + + seq_printf(seq, "%d %-10s %-10s\n", + f->lci, f->dev1->name, f->dev2->name); +out: + return 0; +} + +static const struct seq_operations x25_seq_route_ops = { .start = x25_seq_route_start, .next = x25_seq_route_next, .stop = x25_seq_route_stop, .show = x25_seq_route_show, }; -static struct seq_operations x25_seq_socket_ops = { +static const struct seq_operations x25_seq_socket_ops = { .start = x25_seq_socket_start, .next = x25_seq_socket_next, .stop = x25_seq_socket_stop, .show = x25_seq_socket_show, }; +static const struct seq_operations x25_seq_forward_ops = { + .start = x25_seq_forward_start, + .next = x25_seq_forward_next, + .stop = x25_seq_forward_stop, + .show = x25_seq_forward_show, +}; + static int x25_seq_socket_open(struct inode *inode, struct file *file) { return seq_open(file, &x25_seq_socket_ops); @@ -190,57 +181,58 @@ static int x25_seq_route_open(struct inode *inode, struct file *file) return seq_open(file, &x25_seq_route_ops); } -static struct file_operations x25_seq_socket_fops = { - .owner = THIS_MODULE, +static int x25_seq_forward_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &x25_seq_forward_ops); +} + +static const struct file_operations x25_seq_socket_fops = { .open = x25_seq_socket_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; -static struct file_operations x25_seq_route_fops = { - .owner = THIS_MODULE, +static const struct file_operations x25_seq_route_fops = { .open = x25_seq_route_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; -static struct proc_dir_entry *x25_proc_dir; +static const struct file_operations x25_seq_forward_fops = { + .open = x25_seq_forward_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; int __init x25_proc_init(void) { - struct proc_dir_entry *p; - int rc = -ENOMEM; + if (!proc_mkdir("x25", init_net.proc_net)) + return -ENOMEM; + + if (!proc_create("x25/route", S_IRUGO, init_net.proc_net, + &x25_seq_route_fops)) + goto out; - x25_proc_dir = proc_mkdir("x25", proc_net); - if (!x25_proc_dir) + if (!proc_create("x25/socket", S_IRUGO, init_net.proc_net, + &x25_seq_socket_fops)) goto out; - p = create_proc_entry("route", S_IRUGO, x25_proc_dir); - if (!p) - goto out_route; - p->proc_fops = &x25_seq_route_fops; + if (!proc_create("x25/forward", S_IRUGO, init_net.proc_net, + &x25_seq_forward_fops)) + goto out; + return 0; - p = create_proc_entry("socket", S_IRUGO, x25_proc_dir); - if (!p) - goto out_socket; - p->proc_fops = &x25_seq_socket_fops; - rc = 0; out: - return rc; -out_socket: - remove_proc_entry("route", x25_proc_dir); -out_route: - remove_proc_entry("x25", proc_net); - goto out; + remove_proc_subtree("x25", init_net.proc_net); + return -ENOMEM; } void __exit x25_proc_exit(void) { - remove_proc_entry("route", x25_proc_dir); - remove_proc_entry("socket", x25_proc_dir); - remove_proc_entry("x25", proc_net); + remove_proc_subtree("x25", init_net.proc_net); } #else /* CONFIG_PROC_FS */ diff --git a/net/x25/x25_route.c b/net/x25/x25_route.c index 6c5d3751703..277c8d2448d 100644 --- a/net/x25/x25_route.c +++ b/net/x25/x25_route.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 * @@ -17,12 +17,12 @@ * X.25 001 Jonathan Naylor Started coding. */ -#include <linux/config.h> #include <linux/if_arp.h> #include <linux/init.h> +#include <linux/slab.h> #include <net/x25.h> -struct list_head x25_route_list = LIST_HEAD_INIT(x25_route_list); +LIST_HEAD(x25_route_list); DEFINE_RWLOCK(x25_route_list_lock); /* @@ -66,7 +66,7 @@ out: /** * __x25_remove_route - remove route from x25_route_list - * @rt - route to remove + * @rt: route to remove * * Remove route from x25_route_list. If it was there. * Caller must hold x25_route_list_lock. @@ -120,6 +120,9 @@ void x25_route_device_down(struct net_device *dev) __x25_remove_route(rt); } write_unlock_bh(&x25_route_list_lock); + + /* Remove any related forwarding */ + x25_clear_forward_by_dev(dev); } /* @@ -127,15 +130,17 @@ void x25_route_device_down(struct net_device *dev) */ struct net_device *x25_dev_get(char *devname) { - struct net_device *dev = dev_get_by_name(devname); + struct net_device *dev = dev_get_by_name(&init_net, devname); if (dev && (!(dev->flags & IFF_UP) || (dev->type != ARPHRD_X25 -#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) +#if IS_ENABLED(CONFIG_LLC) && dev->type != ARPHRD_ETHER #endif - ))) + ))){ dev_put(dev); + dev = NULL; + } return dev; } @@ -188,7 +193,7 @@ int x25_route_ioctl(unsigned int cmd, void __user *arg) goto out; rc = -EINVAL; - if (rt.sigdigits < 0 || rt.sigdigits > 15) + if (rt.sigdigits > 15) goto out; dev = x25_dev_get(rt.device); diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c index 8be9b8fbc24..6b5af65f491 100644 --- a/net/x25/x25_subr.c +++ b/net/x25/x25_subr.c @@ -23,6 +23,9 @@ * restriction on response. */ +#define pr_fmt(fmt) "X25: " fmt + +#include <linux/slab.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/skbuff.h> @@ -125,32 +128,30 @@ void x25_write_internal(struct sock *sk, int frametype) * Adjust frame size. */ switch (frametype) { - case X25_CALL_REQUEST: - len += 1 + X25_ADDR_LEN + X25_MAX_FAC_LEN + - X25_MAX_CUD_LEN; - break; - case X25_CALL_ACCEPTED: /* fast sel with no restr on resp */ - if(x25->facilities.reverse & 0x80) { - len += 1 + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN; - } else { - len += 1 + X25_MAX_FAC_LEN; - } - break; - case X25_CLEAR_REQUEST: - case X25_RESET_REQUEST: - len += 2; - break; - case X25_RR: - case X25_RNR: - case X25_REJ: - case X25_CLEAR_CONFIRMATION: - case X25_INTERRUPT_CONFIRMATION: - case X25_RESET_CONFIRMATION: - break; - default: - printk(KERN_ERR "X.25: invalid frame type %02X\n", - frametype); - return; + case X25_CALL_REQUEST: + len += 1 + X25_ADDR_LEN + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN; + break; + case X25_CALL_ACCEPTED: /* fast sel with no restr on resp */ + if (x25->facilities.reverse & 0x80) { + len += 1 + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN; + } else { + len += 1 + X25_MAX_FAC_LEN; + } + break; + case X25_CLEAR_REQUEST: + case X25_RESET_REQUEST: + len += 2; + break; + case X25_RR: + case X25_RNR: + case X25_REJ: + case X25_CLEAR_CONFIRMATION: + case X25_INTERRUPT_CONFIRMATION: + case X25_RESET_CONFIRMATION: + break; + default: + pr_err("invalid frame type %02X\n", frametype); + return; } if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) @@ -190,8 +191,9 @@ void x25_write_internal(struct sock *sk, int frametype) dptr = skb_put(skb, len); memcpy(dptr, addresses, len); len = x25_create_facilities(facilities, - &x25->facilities, - x25->neighbour->global_facil_mask); + &x25->facilities, + &x25->dte_facilities, + x25->neighbour->global_facil_mask); dptr = skb_put(skb, len); memcpy(dptr, facilities, len); dptr = skb_put(skb, x25->calluserdata.cudlength); @@ -206,6 +208,7 @@ void x25_write_internal(struct sock *sk, int frametype) *dptr++ = 0x00; /* Address lengths */ len = x25_create_facilities(facilities, &x25->facilities, + &x25->dte_facilities, x25->vc_facil_mask); dptr = skb_put(skb, len); memcpy(dptr, facilities, len); @@ -223,6 +226,12 @@ void x25_write_internal(struct sock *sk, int frametype) break; case X25_CLEAR_REQUEST: + dptr = skb_put(skb, 3); + *dptr++ = frametype; + *dptr++ = x25->causediag.cause; + *dptr++ = x25->causediag.diagnostic; + break; + case X25_RESET_REQUEST: dptr = skb_put(skb, 3); *dptr++ = frametype; @@ -262,31 +271,39 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) { struct x25_sock *x25 = x25_sk(sk); - unsigned char *frame = skb->data; + unsigned char *frame; + + if (!pskb_may_pull(skb, X25_STD_MIN_LEN)) + return X25_ILLEGAL; + frame = skb->data; *ns = *nr = *q = *d = *m = 0; switch (frame[2]) { - case X25_CALL_REQUEST: - case X25_CALL_ACCEPTED: - case X25_CLEAR_REQUEST: - case X25_CLEAR_CONFIRMATION: - case X25_INTERRUPT: - case X25_INTERRUPT_CONFIRMATION: - case X25_RESET_REQUEST: - case X25_RESET_CONFIRMATION: - case X25_RESTART_REQUEST: - case X25_RESTART_CONFIRMATION: - case X25_REGISTRATION_REQUEST: - case X25_REGISTRATION_CONFIRMATION: - case X25_DIAGNOSTIC: - return frame[2]; + case X25_CALL_REQUEST: + case X25_CALL_ACCEPTED: + case X25_CLEAR_REQUEST: + case X25_CLEAR_CONFIRMATION: + case X25_INTERRUPT: + case X25_INTERRUPT_CONFIRMATION: + case X25_RESET_REQUEST: + case X25_RESET_CONFIRMATION: + case X25_RESTART_REQUEST: + case X25_RESTART_CONFIRMATION: + case X25_REGISTRATION_REQUEST: + case X25_REGISTRATION_CONFIRMATION: + case X25_DIAGNOSTIC: + return frame[2]; } if (x25->neighbour->extended) { if (frame[2] == X25_RR || frame[2] == X25_RNR || frame[2] == X25_REJ) { + if (!pskb_may_pull(skb, X25_EXT_MIN_LEN)) + return X25_ILLEGAL; + frame = skb->data; + *nr = (frame[3] >> 1) & 0x7F; return frame[2]; } @@ -301,6 +318,10 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, if (x25->neighbour->extended) { if ((frame[2] & 0x01) == X25_DATA) { + if (!pskb_may_pull(skb, X25_EXT_MIN_LEN)) + return X25_ILLEGAL; + frame = skb->data; + *q = (frame[0] & X25_Q_BIT) == X25_Q_BIT; *d = (frame[0] & X25_D_BIT) == X25_D_BIT; *m = (frame[3] & X25_EXT_M_BIT) == X25_EXT_M_BIT; @@ -319,7 +340,7 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, } } - printk(KERN_DEBUG "X.25: invalid PLP frame %02X %02X %02X\n", + pr_debug("invalid PLP frame %02X %02X %02X\n", frame[0], frame[1], frame[2]); return X25_ILLEGAL; @@ -357,7 +378,7 @@ void x25_check_rbuf(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf / 2) && + if (atomic_read(&sk->sk_rmem_alloc) < (sk->sk_rcvbuf >> 1) && (x25->condition & X25_COND_OWN_RX_BUSY)) { x25->condition &= ~X25_COND_OWN_RX_BUSY; x25->condition &= ~X25_COND_ACK_PENDING; diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c index 0a92e1da392..5c5db1a3639 100644 --- a/net/x25/x25_timer.c +++ b/net/x25/x25_timer.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 * @@ -33,9 +33,7 @@ void x25_init_timers(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - init_timer(&x25->timer); - x25->timer.data = (unsigned long)sk; - x25->timer.function = &x25_timer_expiry; + setup_timer(&x25->timer, x25_timer_expiry, (unsigned long)sk); /* initialized by sock_init_data */ sk->sk_timer.data = (unsigned long)sk; @@ -99,8 +97,8 @@ static void x25_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; - bh_lock_sock(sk); - if (sock_owned_by_user(sk)) /* can currently only occur in state 3 */ + bh_lock_sock(sk); + if (sock_owned_by_user(sk)) /* can currently only occur in state 3 */ goto restart_heartbeat; switch (x25_sk(sk)->state) { @@ -114,8 +112,9 @@ static void x25_heartbeat_expiry(unsigned long param) if (sock_flag(sk, SOCK_DESTROY) || (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { - x25_destroy_socket(sk); - goto unlock; + bh_unlock_sock(sk); + x25_destroy_socket_from_timer(sk); + return; } break; @@ -128,7 +127,6 @@ static void x25_heartbeat_expiry(unsigned long param) } restart_heartbeat: x25_start_heartbeat(sk); -unlock: bh_unlock_sock(sk); } |
