patch-2.1.22 linux/net/x25/af_x25.c

Next file: linux/net/x25/x25_dev.c
Previous file: linux/net/unix/af_unix.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.21/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
@@ -15,7 +15,7 @@
  *	History
  *	X.25 001	Jonathan Naylor	Started coding.
  */
-  
+
 #include <linux/config.h>
 #if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
 #include <linux/module.h>
@@ -107,9 +107,9 @@
 
 	called_len  = strlen(called);
 	calling_len = strlen(calling);
-	
+
 	*p++ = (calling_len << 4) | (called_len << 0);
-	
+
 	for (i = 0; i < (called_len + calling_len); i++) {
 		if (i < called_len) {
 			if (i % 2 != 0) {
@@ -129,16 +129,38 @@
 			}
 		}
 	}
-	
+
 	return 1 + (called_len + calling_len + 1) / 2;
 }
 
 /*
  *	Socket removal during an interrupt is now safe.
  */
-extern inline void x25_remove_socket(struct sock *sk)
+static void x25_remove_socket(struct sock *sk)
 {
-	sklist_remove_socket(&x25_list,sk);
+	struct sock *s;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if ((s = x25_list) == sk) {
+		x25_list = s->next;
+		restore_flags(flags);
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == sk) {
+			s->next = sk->next;
+			restore_flags(flags);
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
 }
 
 /*
@@ -147,7 +169,7 @@
 static void x25_kill_by_device(struct device *dev)
 {
 	struct sock *s;
-	
+
 	for (s = x25_list; s != NULL; s = s->next) {
 		if (s->protinfo.x25->neighbour->dev == dev) {
 			s->protinfo.x25->state  = X25_STATE_0;
@@ -190,10 +212,17 @@
 /*
  *	Add a socket to the bound sockets list.
  */
- 
-extern inline void x25_insert_socket(struct sock *sk)
+static void x25_insert_socket(struct sock *sk)
 {
-	sklist_insert_socket(&x25_list,sk);
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	sk->next = x25_list;
+	x25_list = sk;
+
+	restore_flags(flags);
 }
 
 /*
@@ -263,7 +292,7 @@
 void x25_destroy_socket(struct sock *);
 
 /*
- *	Handler for deferred kills.
+ *	handler for deferred kills.
  */
 static void x25_destroy_timer(unsigned long data)
 {
@@ -280,15 +309,15 @@
 {
 	struct sk_buff *skb;
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
-	
+
 	del_timer(&sk->timer);
-	
+
 	x25_remove_socket(sk);
 	x25_clear_queues(sk);		/* Flush the queues */
-	
+
 	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
 		if (skb->sk != sk) {		/* A pending connection */
 			skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
@@ -298,8 +327,8 @@
 
 		kfree_skb(skb, FREE_READ);
 	}
-	
-	if (sk->wmem_alloc || sk->rmem_alloc) { /* Defer: outstanding buffers */
+
+	if (sk->wmem_alloc || sk->rmem_alloc) {	/* Defer: outstanding buffers */
 		init_timer(&sk->timer);
 		sk->timer.expires  = jiffies + 10 * HZ;
 		sk->timer.function = x25_destroy_timer;
@@ -318,20 +347,13 @@
  *	Handling for system calls applied via the various interfaces to a
  *	X.25 socket object.
  */
- 
-static int x25_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-	return -EINVAL;
-}
 
 static int x25_setsockopt(struct socket *sock, int level, int optname,
 	char *optval, int optlen)
 {
-	struct sock *sk;
+	struct sock *sk = sock->sk;
 	int err, opt;
 
-	sk = (struct sock *)sock->sk;
-	
 	if (level != SOL_X25)
 		return -EOPNOTSUPP;
 
@@ -342,49 +364,12 @@
 		return err;
 
 	get_user(opt, (int *)optval);
-	
+
 	switch (optname) {
 		case X25_QBITINCL:
 			sk->protinfo.x25->qbitincl = opt ? 1 : 0;
 			return 0;
 
-		case X25_PACKET_SIZE:
-			if (sk->state != TCP_LISTEN)
-				return -EINVAL;
-			if (opt < X25_PS16 || opt > X25_PS4096)
-				return -EINVAL;
-			sk->protinfo.x25->facilities.packet_size = opt;
-			return 0;
-
-		case X25_WINDOW_SIZE:
-			if (sk->state != TCP_LISTEN)
-				return -EINVAL;
-			if (sk->protinfo.x25->neighbour->extended) {
-				if (opt < 1 || opt > 127)
-					return -EINVAL;
-			} else {
-				if (opt < 1 || opt > 7)
-					return -EINVAL;
-			}
-			sk->protinfo.x25->facilities.window_size = opt;
-			return 0;
-
-		case X25_THROUGHPUT_SPEED:
-			if (sk->state != TCP_LISTEN)
-				return -EINVAL;
-			if (opt < 0x03 || opt > 0x2C)
-				return -EINVAL;
-			sk->protinfo.x25->facilities.throughput = opt;
-			return 0;
-
-		case X25_REVERSE_CHARGE:
-			if (sk->state != TCP_LISTEN)
-				return -EINVAL;
-			if (opt != 0 && opt != 1)
-				return -EINVAL;
-			sk->protinfo.x25->facilities.reverse = opt;
-			return 0;
-
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -393,36 +378,18 @@
 static int x25_getsockopt(struct socket *sock, int level, int optname,
 	char *optval, int *optlen)
 {
-	struct sock *sk;
+	struct sock *sk = sock->sk;
 	int val = 0;
 	int err; 
 
-	sk = (struct sock *)sock->sk;
-	
 	if (level != SOL_X25)
 		return -EOPNOTSUPP;
-	
+
 	switch (optname) {
 		case X25_QBITINCL:
 			val = sk->protinfo.x25->qbitincl;
 			break;
 
-		case X25_PACKET_SIZE:
-			val = sk->protinfo.x25->facilities.packet_size;
-			break;
-			
-		case X25_WINDOW_SIZE:
-			val = sk->protinfo.x25->facilities.window_size;
-			break;
-
-		case X25_THROUGHPUT_SPEED:
-			val = sk->protinfo.x25->facilities.throughput;
-			break;
-
-		case X25_REVERSE_CHARGE:
-			val = sk->protinfo.x25->facilities.reverse;
-			break;
-
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -442,7 +409,7 @@
 
 static int x25_listen(struct socket *sock, int backlog)
 {
-	struct sock *sk = (struct sock *)sock->sk;
+	struct sock *sk = sock->sk;
 
 	if (sk->state != TCP_LISTEN) {
 		memset(&sk->protinfo.x25->dest_addr, '\0', X25_ADDR_LEN);
@@ -462,8 +429,18 @@
 
 static void def_callback2(struct sock *sk, int len)
 {
-	if (!sk->dead)
+	if (!sk->dead) {
 		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket, 1);
+	}
+}
+
+static void def_callback3(struct sock *sk)
+{
+	if (!sk->dead) {
+		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket, 2);
+	}
 }
 
 static struct sock *x25_alloc_socket(void)
@@ -494,12 +471,12 @@
 
 	sk->state_change = def_callback1;
 	sk->data_ready   = def_callback2;
-	sk->write_space  = def_callback1;
+	sk->write_space  = def_callback3;
 	sk->error_report = def_callback1;
 
-	skb_queue_head_init(&x25->ack_queue);
 	skb_queue_head_init(&x25->fragment_queue);
-	skb_queue_head_init(&x25->interrupt_queue);
+	skb_queue_head_init(&x25->interrupt_in_queue);
+	skb_queue_head_init(&x25->interrupt_out_queue);
 
 	return sk;
 }
@@ -542,8 +519,10 @@
 
 	x25->state       = X25_STATE_0;
 
-	x25->facilities.window_size = X25_DEFAULT_WINDOW_SIZE;
-	x25->facilities.packet_size = X25_DEFAULT_PACKET_SIZE;
+	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.reverse     = X25_DEFAULT_REVERSE;
 
@@ -589,14 +568,17 @@
 
 static int x25_dup(struct socket *newsock, struct socket *oldsock)
 {
-	struct sock *sk = (struct sock *)oldsock->sk;
+	struct sock *sk = oldsock->sk;
+
+	if (sk == NULL || newsock == NULL)
+		return -EINVAL;
 
 	return x25_create(newsock, sk->protocol);
 }
 
 static int x25_release(struct socket *sock, struct socket *peer)
 {
-	struct sock *sk = (struct sock *)sock->sk;
+	struct sock *sk = sock->sk;
 
 	if (sk == NULL) return 0;
 
@@ -653,14 +635,12 @@
 
 static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
-	struct sock *sk;
+	struct sock *sk = sock->sk;
 	struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr;
-	
-	sk = (struct sock *)sock->sk;
 
 	if (sk->zapped == 0)
 		return -EINVAL;
-		
+
 	if (addr_len != sizeof(struct sockaddr_x25))
 		return -EINVAL;
 
@@ -681,29 +661,32 @@
 
 static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
 {
-	struct sock *sk = (struct sock *)sock->sk;
+	struct sock *sk = sock->sk;
 	struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr;
 	struct device *dev;
-	
+
 	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
 		return 0;	/* Connect completed during a ERESTARTSYS event */
 	}
-	
+
 	if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
 		sock->state = SS_UNCONNECTED;
 		return -ECONNREFUSED;
 	}
-	
+
 	if (sk->state == TCP_ESTABLISHED)
 		return -EISCONN;	/* No reconnect on a seqpacket socket */
-		
+
 	sk->state   = TCP_CLOSE;	
 	sock->state = SS_UNCONNECTED;
 
 	if (addr_len != sizeof(struct sockaddr_x25))
 		return -EINVAL;
 
+	if (addr->sx25_family != AF_X25)
+		return -EINVAL;
+
 	if ((dev = x25_get_route(&addr->sx25_addr)) == NULL)
 		return -ENETUNREACH;
 
@@ -725,11 +708,11 @@
 	x25_write_internal(sk, X25_CALL_REQUEST);
 
 	x25_set_timer(sk);
-	
+
 	/* Now the loop */
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
 		return -EINPROGRESS;
-		
+
 	cli();	/* To avoid races on the sleep */
 
 	/*
@@ -748,11 +731,11 @@
 		sock->state = SS_UNCONNECTED;
 		return sock_error(sk);	/* Always set at this point */
 	}
-	
+
 	sock->state = SS_CONNECTED;
 
 	sti();
-	
+
 	return 0;
 }
 	
@@ -767,19 +750,20 @@
 	struct sock *newsk;
 	struct sk_buff *skb;
 
-	if (newsock->sk)
-		sk_free(newsock->sk);
+	if (newsock->sk != NULL)
+		x25_destroy_socket(newsock->sk);
 
 	newsock->sk = NULL;
-	
-	sk = (struct sock *)sock->sk;
+
+	if ((sk = sock->sk) == NULL)
+		return -EINVAL;
 
 	if (sk->type != SOCK_SEQPACKET)
 		return -EOPNOTSUPP;
-	
+
 	if (sk->state != TCP_LISTEN)
 		return -EINVAL;
-		
+
 	/*
 	 *	The write queue this time is holding sockets ready to use
 	 *	hooked into the CALL INDICATION we saved
@@ -789,7 +773,7 @@
 		if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
 			if (flags & O_NONBLOCK) {
 				sti();
-				return 0;
+				return -EWOULDBLOCK;
 			}
 			interruptible_sleep_on(sk->sleep);
 			if (current->signal & ~current->blocked) {
@@ -815,10 +799,8 @@
 static int x25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
 {
 	struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr;
-	struct sock *sk;
-	
-	sk = (struct sock *)sock->sk;
-	
+	struct sock *sk = sock->sk;
+
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
@@ -839,11 +821,6 @@
 	struct sock *make;
 	x25_address source_addr, dest_addr;
 	struct x25_facilities facilities;
-	int len;
-
-	/*
-	 *	skb->data points to the x25 frame start
-	 */
 
 	/*
 	 *	Remove the LCI and frame type.
@@ -851,14 +828,10 @@
 	skb_pull(skb, X25_STD_MIN_LEN);
 
 	/*
-	 *	Extract the X.25 addresses and convert them to ASCII strings.
+	 *	Extract the X.25 addresses and convert them to ASCII strings,
+	 *	and remove them.
 	 */
-	len = x25_addr_ntoa(skb->data, &source_addr, &dest_addr);
-
-	/*
-	 *	Remove address lengths and addresses.
-	 */
-	skb_pull(skb, len);
+	skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
 
 	/*
 	 *	Find a listener for the particular address.
@@ -874,14 +847,10 @@
 	}
 
 	/*
-	 *	Parse the facilities.
-	 */
-	len = x25_parse_facilities(skb, &facilities);
-
-	/*
-	 *	Then remove them, leaving any Call User Data.
+	 *	Parse the facilities, and remove them, leaving any Call User
+	 *	Data.
 	 */
-	skb_pull(skb, len);
+	skb_pull(skb, x25_parse_facilities(skb, &facilities));
 
 	skb->sk     = make;
 	make->state = TCP_ESTABLISHED;
@@ -897,11 +866,15 @@
 	 */
 	make->protinfo.x25->facilities    = facilities;
 
+	x25_write_internal(make, X25_CALL_ACCEPTED);
+
 	/*
-	 *	Incoming Call User Data. XXX
+	 *	Incoming Call User Data.
 	 */
-	
-	x25_write_internal(make, X25_CALL_ACCEPTED);
+	if (skb->len >= 0) {
+		memcpy(make->protinfo.x25->calluserdata.cuddata, skb->data, skb->len);
+		make->protinfo.x25->calluserdata.cudlength = skb->len;
+	}
 
 	make->protinfo.x25->state = X25_STATE_3;
 
@@ -922,7 +895,7 @@
 
 static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->sk;
+	struct sock *sk = sock->sk;
 	struct sockaddr_x25 *usx25 = (struct sockaddr_x25 *)msg->msg_name;
 	int err;
 	struct sockaddr_x25 sx25;
@@ -930,7 +903,7 @@
 	unsigned char *asmptr;
 	int size, qbit = 0;
 
-	if (msg->msg_flags & ~MSG_DONTWAIT)
+	if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_OOB))
 		return -EINVAL;
 
 	if (sk->zapped)
@@ -943,7 +916,7 @@
 
 	if (sk->protinfo.x25->neighbour == NULL)
 		return -ENETUNREACH;
-		
+
 	if (usx25 != NULL) {
 		if (msg->msg_namelen < sizeof(sx25))
 			return -EINVAL;
@@ -964,7 +937,7 @@
 		sx25.sx25_family = AF_X25;
 		sx25.sx25_addr   = sk->protinfo.x25->dest_addr;
 	}
-	
+
 	if (sk->debug)
 		printk(KERN_DEBUG "x25_sendmsg: sendto: Addresses built.\n");
 
@@ -972,16 +945,16 @@
 	if (sk->debug)
 		printk(KERN_DEBUG "x25_sendmsg: sendto: building packet.\n");
 
+	if ((msg->msg_flags & MSG_OOB) && len > 32)
+		len = 32;
+
 	size = len + X25_MAX_L2_LEN + X25_EXT_MIN_LEN;
 
 	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
-	skb->sk   = sk;
-	skb->arp  = 1;
-
 	skb_reserve(skb, X25_MAX_L2_LEN + X25_EXT_MIN_LEN);
-	
+
 	/*
 	 *	Put the data on the end
 	 */
@@ -1005,26 +978,40 @@
 	/*
 	 *	Push down the X.25 header
 	 */
-	if (sk->debug)
+ 	if (sk->debug)
 		printk(KERN_DEBUG "x25_sendmsg: Building X.25 Header.\n");
 
-	if (sk->protinfo.x25->neighbour->extended) {
-		/* Build an Extended X.25 header */
-		asmptr    = skb_push(skb, X25_EXT_MIN_LEN);
-		*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ;
-		*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
-		*asmptr++ = X25_DATA;
-		*asmptr++ = X25_DATA;
+	if (msg->msg_flags & MSG_OOB) {
+		if (sk->protinfo.x25->neighbour->extended) {
+			asmptr    = skb_push(skb, X25_STD_MIN_LEN);
+			*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ;
+			*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+			*asmptr++ = X25_INTERRUPT;
+		} else {
+			asmptr    = skb_push(skb, X25_STD_MIN_LEN);
+			*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ;
+			*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+			*asmptr++ = X25_INTERRUPT;
+		}
 	} else {
-		/* Build an Standard X.25 header */
-		asmptr    = skb_push(skb, X25_STD_MIN_LEN);
-		*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ;
-		*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
-		*asmptr++ = X25_DATA;
-	}
+		if (sk->protinfo.x25->neighbour->extended) {
+			/* Build an Extended X.25 header */
+			asmptr    = skb_push(skb, X25_EXT_MIN_LEN);
+			*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ;
+			*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+			*asmptr++ = X25_DATA;
+			*asmptr++ = X25_DATA;
+		} else {
+			/* Build an Standard X.25 header */
+			asmptr    = skb_push(skb, X25_STD_MIN_LEN);
+			*asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ;
+			*asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+			*asmptr++ = X25_DATA;
+		}
 
-	if (qbit)
-		skb->data[0] |= X25_Q_BIT;
+		if (qbit)
+			skb->data[0] |= X25_Q_BIT;
+	}
 
 	if (sk->debug)
 		printk(KERN_DEBUG "x25_sendmsg: Built header.\n");
@@ -1037,7 +1024,14 @@
 		return -ENOTCONN;
 	}
 
-	x25_output(sk, skb);	/* Shove it onto the queue */
+	if (msg->msg_flags & MSG_OOB) {
+		skb_queue_tail(&sk->protinfo.x25->interrupt_out_queue, skb);
+	} else {
+		x25_output(sk, skb);
+	}
+
+	if (sk->protinfo.x25->state == X25_STATE_3)
+		x25_kick(sk);
 
 	return len;
 }
@@ -1045,7 +1039,7 @@
 
 static int x25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->sk;
+	struct sock *sk = sock->sk;
 	struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name;
 	int copied, qbit;
 	struct sk_buff *skb;
@@ -1059,37 +1053,52 @@
 	if (sk->state != TCP_ESTABLISHED)
 		return -ENOTCONN;
 
-	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
-		return er;
+	if (flags & MSG_OOB) {
+		if (sk->urginline || skb_peek(&sk->protinfo.x25->interrupt_in_queue) == NULL)
+			return -EINVAL;
 
-	qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT;
+		skb = skb_dequeue(&sk->protinfo.x25->interrupt_in_queue);
 
-	skb_pull(skb, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN);
-	skb->h.raw = skb->data;
+		skb_pull(skb, X25_STD_MIN_LEN);
 
-	if (sk->protinfo.x25->qbitincl) {
-		asmptr  = skb_push(skb, 1);
-		*asmptr = qbit;
-		skb->h.raw = skb->data;
+		/*
+		 *	No Q bit information on Interrupt data.
+		 */
+		if (sk->protinfo.x25->qbitincl) {
+			asmptr  = skb_push(skb, 1);
+			*asmptr = 0x00;
+		}
+
+		msg->msg_flags |= MSG_OOB;
+	} else {
+		/* Now we can treat all alike */
+		if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
+			return er;
+
+		qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT;
+
+		skb_pull(skb, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN);
+
+		if (sk->protinfo.x25->qbitincl) {
+			asmptr  = skb_push(skb, 1);
+			*asmptr = qbit;
+		}
 	}
 
+	skb->h.raw = skb->data;
+
 	copied = skb->len;
-	
+
 	if (copied > size) {
 		copied = size;
 		msg->msg_flags |= MSG_TRUNC;
 	}
 
 	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-	
-	if (sx25 != NULL) {
-		struct sockaddr_x25 addr;
-		
-		addr.sx25_family = AF_X25;
-		addr.sx25_addr   = sk->protinfo.x25->dest_addr;
 
-		*sx25 = addr;
+	if (sx25 != NULL) {
+		sx25->sx25_family = AF_X25;
+		sx25->sx25_addr   = sk->protinfo.x25->dest_addr;
 	}
 
 	msg->msg_namelen = sizeof(struct sockaddr_x25);
@@ -1106,7 +1115,9 @@
 
 static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk = (struct sock *)sock->sk;
+	struct x25_facilities facilities;
+	struct x25_calluserdata calluserdata;
+	struct sock *sk = sock->sk;
 	int err;
 	long amount = 0;
 
@@ -1159,10 +1170,64 @@
 			if (!suser()) return -EPERM;
 			return x25_route_ioctl(cmd, (void *)arg);
 
-		case SIOCX25SETSUBSCR:
+		case SIOCX25GSUBSCRIP:
+			return x25_subscr_ioctl(cmd, (void *)arg);
+
+		case SIOCX25SSUBSCRIP:
 			if (!suser()) return -EPERM;
 			return x25_subscr_ioctl(cmd, (void *)arg);
 
+		case SIOCX25GFACILITIES:
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(facilities))) != 0)
+				return err;
+			facilities = sk->protinfo.x25->facilities;
+			copy_to_user((void *)arg, &facilities, sizeof(facilities));
+			return 0;
+
+		case SIOCX25SFACILITIES:
+			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(facilities))) != 0)
+				return err;
+			copy_from_user(&facilities, (void *)arg, sizeof(facilities));
+			if (sk->state != TCP_LISTEN)
+				return -EINVAL;
+			if (facilities.pacsize_in < X25_PS16 || facilities.pacsize_in > X25_PS4096)
+				return -EINVAL;
+			if (facilities.pacsize_out < X25_PS16 || facilities.pacsize_out > X25_PS4096)
+				return -EINVAL;
+			if (sk->protinfo.x25->neighbour->extended) {
+				if (facilities.winsize_in < 1 || facilities.winsize_in > 127)
+					return -EINVAL;
+				if (facilities.winsize_out < 1 || facilities.winsize_out > 127)
+					return -EINVAL;
+			} else {
+				if (facilities.winsize_in < 1 || facilities.winsize_in > 7)
+					return -EINVAL;
+				if (facilities.winsize_out < 1 || facilities.winsize_out > 7)
+					return -EINVAL;
+			}
+			if (facilities.throughput < 0x03 || facilities.throughput > 0x2C)
+				return -EINVAL;
+			if (facilities.reverse != 0 && facilities.reverse != 1)
+				return -EINVAL;
+			sk->protinfo.x25->facilities = facilities;
+			return 0;
+
+		case SIOCX25GCALLUSERDATA:
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(calluserdata))) != 0)
+				return err;
+			calluserdata = sk->protinfo.x25->calluserdata;
+			copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata));
+			return 0;
+
+		case SIOCX25SCALLUSERDATA:
+			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(calluserdata))) != 0)
+				return err;
+			copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata));
+			if (calluserdata.cudlength > X25_MAX_CUD_LEN)
+				return -EINVAL;
+			sk->protinfo.x25->calluserdata = calluserdata;
+			return 0;
+
  		default:
 			return dev_ioctl(cmd, (void *)arg);
 	}
@@ -1179,7 +1244,7 @@
 	int len = 0;
 	off_t pos = 0;
 	off_t begin = 0;
-  
+
 	cli();
 
 	len += sprintf(buffer, "dest_addr  src_addr   dev   lci st vs vr va   t  t2 t21 t22 t23 Snd-Q Rcv-Q\n");
@@ -1202,14 +1267,14 @@
 			s->protinfo.x25->t22   / X25_SLOWHZ,
 			s->protinfo.x25->t23   / X25_SLOWHZ,
 			s->wmem_alloc, s->rmem_alloc);
-		
+
 		pos = begin + len;
 
 		if (pos < offset) {
 			len   = 0;
 			begin = pos;
 		}
-		
+
 		if (pos > offset + length)
 			break;
 	}
@@ -1245,7 +1310,7 @@
 	x25_shutdown,
 	x25_setsockopt,
 	x25_getsockopt,
-	x25_fcntl,
+	sock_no_fcntl,
 	x25_sendmsg,
 	x25_recvmsg
 };
@@ -1254,7 +1319,7 @@
 {
 	0,		/* MUTTER ntohs(ETH_P_X25),*/
 	0,		/* copy */
-	x25_llc_receive_frame,
+	x25_lapb_receive_frame,
 	NULL,
 	NULL,
 };
@@ -1264,6 +1329,27 @@
 	0
 };
 
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_x25 = {
+	PROC_NET_X25, 3, "x25",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	x25_get_info
+};
+static struct proc_dir_entry proc_net_x25_links = {
+	PROC_NET_X25_LINKS, 9, "x25_links",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	x25_link_get_info
+};
+static struct proc_dir_entry proc_net_x25_routes = {
+	PROC_NET_X25_ROUTES, 10, "x25_routes",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations, 
+	x25_routes_get_info
+};
+#endif	
+
 void x25_proto_init(struct net_proto *pro)
 {
 	sock_register(&x25_family_ops);
@@ -1278,26 +1364,9 @@
 	x25_register_sysctl();
 
 #ifdef CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_X25, 3, "x25",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		x25_get_info
-	});
-
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_X25_LINKS, 9, "x25_links",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		x25_link_get_info
-	});
-
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_X25_ROUTES, 10, "x25_routes",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations, 
-		x25_routes_get_info
-	});
+	proc_net_register(&proc_net_x25);
+	proc_net_register(&proc_net_x25_links);
+	proc_net_register(&proc_net_x25_routes);
 #endif	
 }
 
@@ -1306,7 +1375,20 @@
 
 int init_module(void)
 {
+	struct device *dev;
+
 	x25_proto_init(NULL);
+
+	/*
+	 *	Register any pre existing devices.
+	 */
+	for (dev = dev_base; dev != NULL; dev = dev->next)
+		if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+					   || dev->type == ARPHRD_ETHER
+#endif
+									))
+		x25_link_device_up(dev);
 	
 	return 0;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov