X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fx25%2Faf_x25.c;h=30863a3925b2e857bfa8637b796a2d9fc2162b16;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=17d20343aa850d0087a6dae27c01250860540a73;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 17d20343a..30863a392 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -29,36 +29,34 @@ * 2000-11-14 Henner Eisen Closing datalink from NETDEV_GOING_DOWN * 2002-10-06 Arnaldo C. Melo Get rid of cli/sti, move proc stuff to * x25_proc.c, using seq_file + * 2005-04-02 Shaun Pereira Selective sub address matching + * with call user data + * 2005-04-15 Shaun Pereira Fast select with no restriction on + * response */ -#include #include +#include #include -#include -#include -#include #include #include #include #include -#include #include -#include -#include #include #include #include #include -#include -#include +#include #include #include #include /* For TIOCINQ/OUTQ */ -#include -#include #include #include +#include + #include +#include int sysctl_x25_restart_request_timeout = X25_DEFAULT_T20; int sysctl_x25_call_request_timeout = X25_DEFAULT_T21; @@ -67,12 +65,20 @@ int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23; int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; HLIST_HEAD(x25_list); -rwlock_t x25_list_lock = RW_LOCK_UNLOCKED; +DEFINE_RWLOCK(x25_list_lock); -static struct proto_ops x25_proto_ops; +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_addr_ntoa(unsigned char *p, struct x25_address *called_addr, struct x25_address *calling_addr) { @@ -223,24 +229,46 @@ static void x25_insert_socket(struct sock *sk) /* * Find a socket that wants to accept the Call Request we just - * received. + * received. Check the full list for an address/cud match. + * If no cuds match return the next_best thing, an address match. + * Note: if a listening socket has cud set it must only get calls + * with matching cud. */ -static struct sock *x25_find_listener(struct x25_address *addr) +static struct sock *x25_find_listener(struct x25_address *addr, + struct sk_buff *skb) { 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) if ((!strcmp(addr->x25_addr, - x25_sk(s)->source_addr.x25_addr) || - !strcmp(addr->x25_addr, - null_x25_address.x25_addr)) && - s->sk_state == TCP_LISTEN) { - sock_hold(s); - goto found; + x25_sk(s)->source_addr.x25_addr) || + !strcmp(addr->x25_addr, + null_x25_address.x25_addr)) && + s->sk_state == TCP_LISTEN) { + /* + * 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, + x25_sk(s)->cudmatchlength)) == 0) { + sock_hold(s); + goto found; + } + } else + next_best = s; } + if (next_best) { + s = next_best; + sock_hold(s); + goto found; + } s = NULL; found: read_unlock_bh(&x25_list_lock); @@ -250,7 +278,7 @@ found: /* * Find a connected X.25 socket given my LCI and neighbour. */ -struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb) +static struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb) { struct sock *s; struct hlist_node *node; @@ -278,7 +306,7 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb) /* * Find a unique LCI for a given device. */ -unsigned int x25_new_lci(struct x25_neigh *nb) +static unsigned int x25_new_lci(struct x25_neigh *nb) { unsigned int lci = 1; struct sock *sk; @@ -347,6 +375,7 @@ void x25_destroy_socket(struct sock *sk) /* Defer: outstanding buffers */ sk->sk_timer.expires = jiffies + 10 * HZ; sk->sk_timer.function = x25_destroy_timer; + sk->sk_timer.data = (unsigned long)sk; add_timer(&sk->sk_timer); } else { /* drop last reference so sock_put will free */ @@ -430,43 +459,35 @@ static int x25_listen(struct socket *sock, int backlog) return rc; } +static struct proto x25_proto = { + .name = "X25", + .owner = THIS_MODULE, + .obj_size = sizeof(struct x25_sock), +}; + static struct sock *x25_alloc_socket(void) { - struct x25_opt *x25; - struct sock *sk = sk_alloc(AF_X25, GFP_ATOMIC, 1, NULL); + struct x25_sock *x25; + struct sock *sk = sk_alloc(AF_X25, GFP_ATOMIC, &x25_proto, 1); if (!sk) goto out; - x25 = sk->sk_protinfo = kmalloc(sizeof(*x25), GFP_ATOMIC); - if (!x25) - goto frees; - - memset(x25, 0, sizeof(*x25)); - - x25->sk = sk; - sock_init_data(NULL, sk); - sk_set_owner(sk, THIS_MODULE); + x25 = x25_sk(sk); skb_queue_head_init(&x25->ack_queue); skb_queue_head_init(&x25->fragment_queue); skb_queue_head_init(&x25->interrupt_in_queue); skb_queue_head_init(&x25->interrupt_out_queue); out: return sk; -frees: - sk_free(sk); - sk = NULL; - goto out; } -void x25_init_timers(struct sock *sk); - static int x25_create(struct socket *sock, int protocol) { struct sock *sk; - struct x25_opt *x25; + struct x25_sock *x25; int rc = -ESOCKTNOSUPPORT; if (sock->type != SOCK_SEQPACKET || protocol) @@ -478,8 +499,10 @@ static int x25_create(struct socket *sock, int protocol) x25 = x25_sk(sk); - sock_init_data(sock, sk); - sk_set_owner(sk, THIS_MODULE); + sk->sk_socket = sock; + sk->sk_type = sock->type; + sk->sk_sleep = &sock->wait; + sock->sk = sk; x25_init_timers(sk); @@ -492,6 +515,9 @@ static int x25_create(struct socket *sock, int protocol) x25->t23 = sysctl_x25_clear_request_timeout; x25->t2 = sysctl_x25_ack_holdback_timeout; x25->state = X25_STATE_0; + x25->cudmatchlength = 0; + x25->accptapprv = X25_DENY_ACCPT_APPRV; /* normally no cud */ + /* on call accept */ x25->facilities.winsize_in = X25_DEFAULT_WINDOW_SIZE; x25->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE; @@ -499,6 +525,13 @@ static int x25_create(struct socket *sock, int protocol) x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE; x25->facilities.throughput = X25_DEFAULT_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; @@ -507,7 +540,7 @@ out: static struct sock *x25_make_new(struct sock *osk) { struct sock *sk = NULL; - struct x25_opt *x25, *ox25; + struct x25_sock *x25, *ox25; if (osk->sk_type != SOCK_SEQPACKET) goto out; @@ -523,11 +556,10 @@ static struct sock *x25_make_new(struct sock *osk) sk->sk_protocol = osk->sk_protocol; sk->sk_rcvbuf = osk->sk_rcvbuf; sk->sk_sndbuf = osk->sk_sndbuf; - sk->sk_debug = osk->sk_debug; sk->sk_state = TCP_ESTABLISHED; sk->sk_sleep = osk->sk_sleep; - sk->sk_zapped = osk->sk_zapped; sk->sk_backlog_rcv = osk->sk_backlog_rcv; + sock_copy_flags(sk, osk); ox25 = x25_sk(osk); x25->t21 = ox25->t21; @@ -536,6 +568,9 @@ static struct sock *x25_make_new(struct sock *osk) x25->t2 = ox25->t2; x25->facilities = ox25->facilities; x25->qbitincl = ox25->qbitincl; + x25->dte_facilities = ox25->dte_facilities; + x25->cudmatchlength = ox25->cudmatchlength; + x25->accptapprv = ox25->accptapprv; x25_init_timers(sk); out: @@ -545,7 +580,7 @@ out: static int x25_release(struct socket *sock) { struct sock *sk = sock->sk; - struct x25_opt *x25; + struct x25_sock *x25; if (!sk) goto out; @@ -586,14 +621,14 @@ 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; - if (!sk->sk_zapped || + if (!sock_flag(sk, SOCK_ZAPPED) || addr_len != sizeof(struct sockaddr_x25) || addr->sx25_family != AF_X25) return -EINVAL; x25_sk(sk)->source_addr = addr->sx25_addr; x25_insert_socket(sk); - sk->sk_zapped = 0; + sock_reset_flag(sk, SOCK_ZAPPED); SOCK_DEBUG(sk, "x25_bind: socket is bound\n"); return 0; @@ -632,7 +667,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; - struct x25_opt *x25 = x25_sk(sk); + struct x25_sock *x25 = x25_sk(sk); struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr; struct x25_route *rt; int rc = 0; @@ -677,7 +712,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, goto out_put_neigh; rc = -EINVAL; - if (sk->sk_zapped) /* Must bind first - autobinding does not work */ + if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */ goto out_put_neigh; if (!strcmp(x25->source_addr.x25_addr, null_x25_address.x25_addr)) @@ -717,7 +752,7 @@ 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; @@ -769,7 +804,6 @@ static int x25_accept(struct socket *sock, struct socket *newsock, int flags) if (!skb->sk) goto out2; newsk = skb->sk; - newsk->sk_pair = NULL; newsk->sk_socket = newsock; newsk->sk_sleep = &newsock->wait; @@ -791,7 +825,7 @@ 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_opt *x25 = x25_sk(sk); + struct x25_sock *x25 = x25_sk(sk); if (peer) { if (sk->sk_state != TCP_ESTABLISHED) @@ -811,9 +845,10 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, { struct sock *sk; struct sock *make; - struct x25_opt *makex25; + struct x25_sock *makex25; struct x25_address source_addr, dest_addr; struct x25_facilities facilities; + struct x25_dte_facilities dte_facilities; int len, rc; /* @@ -828,20 +863,30 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr)); /* - * Find a listener for the particular address. + * 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 + */ + len = skb->data[0] + 1; + skb_pull(skb,len); + + /* + * Find a listener for the particular address/cud pair. */ - sk = x25_find_listener(&source_addr); + sk = x25_find_listener(&source_addr,skb); + skb_push(skb,len); /* * We can't accept the Call Request. */ - if (!sk || sk->sk_ack_backlog == sk->sk_max_ack_backlog) + if (sk == NULL || sk_acceptq_is_full(sk)) 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; /* @@ -859,7 +904,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, goto out_sock_put; /* - * Remove the facilities, leaving any Call User Data. + * Remove the facilities */ skb_pull(skb, len); @@ -872,9 +917,19 @@ 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; - - x25_write_internal(make, X25_CALL_ACCEPTED); + /* 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) { + x25_write_internal(make, X25_CALL_ACCEPTED); + makex25->state = X25_STATE_3; + } /* * Incoming Call User Data. @@ -884,10 +939,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, makex25->calluserdata.cudlength = skb->len; } - makex25->state = X25_STATE_3; - sk->sk_ack_backlog++; - make->sk_pair = sk; x25_insert_socket(make); @@ -913,7 +965,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; - struct x25_opt *x25 = x25_sk(sk); + struct x25_sock *x25 = x25_sk(sk); struct sockaddr_x25 *usx25 = (struct sockaddr_x25 *)msg->msg_name; struct sockaddr_x25 sx25; struct sk_buff *skb; @@ -930,7 +982,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, goto out; rc = -EADDRNOTAVAIL; - if (sk->sk_zapped) + if (sock_flag(sk, SOCK_ZAPPED)) goto out; rc = -EPIPE; @@ -1090,7 +1142,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, int flags) { struct sock *sk = sock->sk; - struct x25_opt *x25 = x25_sk(sk); + struct x25_sock *x25 = x25_sk(sk); struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name; size_t copied; int qbit; @@ -1179,7 +1231,7 @@ out: static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - struct x25_opt *x25 = x25_sk(sk); + struct x25_sock *x25 = x25_sk(sk); void __user *argp = (void __user *)arg; int rc; @@ -1269,13 +1321,44 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if (facilities.throughput < 0x03 || facilities.throughput > 0xDD) break; - if (facilities.reverse && facilities.reverse != 1) + if (facilities.reverse && + (facilities.reverse | 0x81)!= 0x81) break; x25->facilities = facilities; rc = 0; break; } + case SIOCX25GDTEFACILITIES: { + rc = copy_to_user(argp, &x25->dte_facilities, + sizeof(x25->dte_facilities)); + if (rc) + rc = -EFAULT; + break; + } + + case SIOCX25SDTEFACILITIES: { + struct x25_dte_facilities dtefacs; + rc = -EFAULT; + if (copy_from_user(&dtefacs, argp, sizeof(dtefacs))) + break; + rc = -EINVAL; + if (sk->sk_state != TCP_LISTEN && + sk->sk_state != TCP_CLOSE) + break; + if (dtefacs.calling_len > X25_MAX_AE_LEN) + break; + if (dtefacs.calling_ae == NULL) + break; + if (dtefacs.called_len > X25_MAX_AE_LEN) + break; + if (dtefacs.called_ae == NULL) + break; + x25->dte_facilities = dtefacs; + rc = 0; + break; + } + case SIOCX25GCALLUSERDATA: { struct x25_calluserdata cud = x25->calluserdata; rc = copy_to_user(argp, &cud, @@ -1306,21 +1389,171 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) break; } + case SIOCX25SCUDMATCHLEN: { + struct x25_subaddr sub_addr; + rc = -EINVAL; + if(sk->sk_state != TCP_CLOSE) + break; + rc = -EFAULT; + if (copy_from_user(&sub_addr, argp, + sizeof(sub_addr))) + break; + rc = -EINVAL; + if(sub_addr.cudmatchlength > X25_MAX_CUD_LEN) + break; + x25->cudmatchlength = sub_addr.cudmatchlength; + rc = 0; + break; + } + + case SIOCX25CALLACCPTAPPRV: { + rc = -EINVAL; + if (sk->sk_state != TCP_CLOSE) + break; + x25->accptapprv = X25_ALLOW_ACCPT_APPRV; + 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; + rc = 0; + break; + } + default: - rc = dev_ioctl(cmd, argp); + rc = -ENOIOCTLCMD; break; } return rc; } -struct net_proto_family x25_family_ops = { +static struct net_proto_family x25_family_ops = { .family = AF_X25, .create = x25_create, .owner = THIS_MODULE, }; -static 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) { + x25_subscr.extended = nb->extended; + x25_subscr.global_facil_mask = nb->global_facil_mask; + 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; + nb->extended = x25_subscr.extended; + nb->global_facil_mask = x25_subscr.global_facil_mask; + } + } + 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 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 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 SOCKOPS_WRAPPED(x25_proto_ops) = { .family = AF_X25, .owner = THIS_MODULE, .release = x25_release, @@ -1331,6 +1564,9 @@ static 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, @@ -1349,7 +1585,7 @@ static struct packet_type x25_packet_type = { .func = x25_lapb_receive_frame, }; -struct notifier_block x25_dev_notifier = { +static struct notifier_block x25_dev_notifier = { .notifier_call = x25_device_event, }; @@ -1369,6 +1605,11 @@ void x25_kill_by_neigh(struct x25_neigh *nb) static int __init x25_init(void) { + int rc = proto_register(&x25_proto, 0); + + if (rc != 0) + goto out; + sock_register(&x25_family_ops); dev_add_pack(&x25_packet_type); @@ -1381,7 +1622,8 @@ static int __init x25_init(void) x25_register_sysctl(); #endif x25_proc_init(); - return 0; +out: + return rc; } module_init(x25_init); @@ -1400,6 +1642,7 @@ static void __exit x25_exit(void) dev_remove_pack(&x25_packet_type); sock_unregister(AF_X25); + proto_unregister(&x25_proto); } module_exit(x25_exit);