patch-2.1.15 linux/net/ipv4/ip_sockglue.c

Next file: linux/net/ipv4/ipip.c
Previous file: linux/net/ipv4/ip_output.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
@@ -36,78 +36,131 @@
 
 #include <asm/uaccess.h>
 
-#ifdef CONFIG_IP_MULTICAST
-
 /*
- *	Write an multicast group list table for the IGMP daemon to
- *	read.
+ *	SOL_IP control messages.
  */
- 
-int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+
+static void ip_cmsg_recv_localdev(struct msghdr *msg, struct sk_buff *skb)
 {
-	off_t pos=0, begin=0;
-	struct ip_mc_list *im;
-	unsigned long flags;
-	int len=0;
-	struct device *dev;
-	
-	len=sprintf(buffer,"Device    : Count\tGroup    Users Timer\n");  
-	save_flags(flags);
-	cli();
-	
-	for(dev = dev_base; dev; dev = dev->next)
-	{
-                if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST))
-                {
-                        len+=sprintf(buffer+len,"%-10s: %5d\n",
-					dev->name, dev->mc_count);
-                        for(im = dev->ip_mc_list; im; im = im->next)
-                        {
-                                len+=sprintf(buffer+len,
-					"\t\t\t%08lX %5d %d:%08lX\n",
-                                        im->multiaddr, im->users,
-					im->tm_running, im->timer.expires-jiffies);
-                                pos=begin+len;
-                                if(pos<offset)
-                                {
-                                        len=0;
-                                        begin=pos;
-                                }
-                                if(pos>offset+length)
-                                        break;
-                        }
-                }
+	static char empty_name[MAX_ADDR_LEN];
+	put_cmsg(msg, SOL_IP, IP_LOCALDEV, MAX_ADDR_LEN,
+		 skb->dev ? skb->dev->name : empty_name);
+}
+
+static void ip_cmsg_recv_localaddr(struct msghdr *msg, struct sk_buff *skb, int local)
+{
+	struct in_addr addr;
+
+	addr.s_addr = skb->nh.iph->daddr;
+
+	if (local) {
+		struct rtable *rt = (struct rtable *)skb->dst;
+		addr.s_addr = rt->rt_spec_dst;
 	}
-	restore_flags(flags);
-	*start=buffer+(offset-begin);
-	len-=(offset-begin);
-	if(len>length)
-		len=length;	
-	return len;
+	put_cmsg(msg, SOL_IP, local ? IP_LOCALADDR : IP_RECVDSTADDR,
+		 sizeof(addr), &addr);
 }
 
+static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
+{
+	if (IPCB(skb)->opt.optlen == 0)
+		return;
+
+	put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1);
+}
 
-/*
- *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
- *	an IP socket.
- *
- *	We implement IP_TOS (type of service), IP_TTL (time to live).
- */
 
-static struct device *ip_mc_find_devfor(unsigned long addr)
+void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
 {
-	struct device *dev;
-	for(dev = dev_base; dev; dev = dev->next)
-	{
-		if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
-			(dev->pa_addr==addr))
-			return dev;
+	unsigned char optbuf[sizeof(struct ip_options) + 40];
+	struct ip_options * opt = (struct ip_options*)optbuf;
+
+	if (IPCB(skb)->opt.optlen == 0)
+		return;
+
+	if (ip_options_echo(opt, skb)) {
+		msg->msg_flags |= MSG_CTRUNC;
+		return;
 	}
+	ip_options_undo(opt);
 
-	return NULL;
+	put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
 }
 
-#endif
+
+void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
+{
+	unsigned flags = skb->sk->ip_cmsg_flags;
+
+	/* Ordered by supposed usage frequency */
+	if (flags & 1)
+		ip_cmsg_recv_localaddr(msg, skb, 1);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_localdev(msg, skb);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_opts(msg, skb);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_retopts(msg, skb);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_localaddr(msg, skb, 0);
+}
+
+int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc, struct device **devp)
+{
+	int err;
+	struct cmsghdr kcm, *cmsg;
+	char	devname[MAX_ADDR_LEN];
+
+	for (cmsg = KCMSG_FIRSTHDR(msg); cmsg; cmsg = KCMSG_NXTHDR(msg, cmsg)) {
+		if (kcm.cmsg_level != SOL_IP)
+			continue;
+		switch (kcm.cmsg_type)
+		{
+		case IP_LOCALADDR:
+			if (kcm.cmsg_len < sizeof(struct in_addr)+sizeof(kcm))
+				return -EINVAL;
+			if (copy_from_user(&ipc->addr, cmsg->cmsg_data, 4))
+				return -EFAULT;
+			break;
+		case IP_RETOPTS:
+			err = kcm.cmsg_len - sizeof(kcm);
+			err = ip_options_getfromuser(&ipc->opt, cmsg->cmsg_data,
+						     err < 40 ? err : 40);
+			if (err)
+				return err;
+			break;
+		case IP_LOCALDEV:
+			if (kcm.cmsg_len < MAX_ADDR_LEN+sizeof(kcm))
+				return -EINVAL;
+			if (!devp)
+				return -EINVAL;
+			if (copy_from_user(devname, cmsg->cmsg_data, MAX_ADDR_LEN-1))
+				return -EFAULT;
+			devname[MAX_ADDR_LEN-1] = 0;
+			if ((*devp = dev_get(devname)) == NULL)
+				return -ENODEV;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
+ *	an IP socket.
+ *
+ *	We implement IP_TOS (type of service), IP_TTL (time to live).
+ */
 
 int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
 {
@@ -143,34 +196,13 @@
 	{
 		case IP_OPTIONS:
 	          {
-			  struct options * opt = NULL;
-			  struct options * old_opt;
+			  struct ip_options * opt = NULL;
+			  struct ip_options * old_opt;
 			  if (optlen > 40 || optlen < 0)
 			  	return -EINVAL;
-			  opt = kmalloc(sizeof(struct options)+((optlen+3)&~3), GFP_KERNEL);
-			  if (!opt)
-			  	return -ENOMEM;
-			  memset(opt, 0, sizeof(struct options));
-			  if (optlen)
-			  {
-				  err = copy_from_user(opt->__data, optval, optlen);
-				  if (err)
-				  {
-					  kfree_s(opt, sizeof(struct options) + ((optlen+3)&~3));
-					  return -EFAULT;
-				  }
-			  }
-
-			  while (optlen & 3)
-			  	opt->__data[optlen++] = IPOPT_END;
-			  opt->optlen = optlen;
-			  opt->is_data = 1;
-			  opt->is_setbyuser = 1;
-			  if (optlen && ip_options_compile(opt, NULL)) 
-			  {
-				  kfree_s(opt, sizeof(struct options) + optlen);
-				  return -EINVAL;
-			  }
+			  err = ip_options_getfromuser(&opt, optval, optlen);
+			  if (err)
+			  	return err;
 			  /*
 			   * ANK: I'm afraid that receive handler may change
 			   * options from under us.
@@ -183,24 +215,44 @@
 			  	kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
 			  return 0;
 		  }
-		case IP_TOS:			/* This sets both TOS and Precedence */
-			if (val & ~0xfe)	/* Reject setting of unused bits */
+		case IP_LOCALADDR:
+			if (val)
+				sk->ip_cmsg_flags |= 1;
+			else
+				sk->ip_cmsg_flags &= ~1;
+			return 0;
+		case IP_LOCALDEV:
+			if (val)
+				sk->ip_cmsg_flags |= 2;
+			else
+				sk->ip_cmsg_flags &= ~2;
+			return 0;
+		case IP_RECVOPTS:
+			if (val)
+				sk->ip_cmsg_flags |= 4;
+			else
+				sk->ip_cmsg_flags &= ~4;
+			return 0;
+		case IP_RETOPTS:
+			if (val)
+				sk->ip_cmsg_flags |= 8;
+			else
+				sk->ip_cmsg_flags &= ~8;
+			return 0;
+		case IP_RECVDSTADDR:
+			if (val)
+				sk->ip_cmsg_flags |= 0x10;
+			else
+				sk->ip_cmsg_flags &= ~0x10;
+			return 0;
+		case IP_TOS:	/* This sets both TOS and Precedence */
+			  /* Reject setting of unused bits */
+			if (val & ~(IPTOS_TOS_MASK|IPTOS_PREC_MASK))
 				return -EINVAL;
-			if ((val>>5) > 4 && !suser())	/* Only root can set Prec>4 */
+			if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !suser())
 				return -EPERM;
 			sk->ip_tos=val;
-			switch (val & 0x1E) {
-				case IPTOS_LOWDELAY:
-					sk->priority=SOPRI_INTERACTIVE;
-					break;
-				case IPTOS_THROUGHPUT:
-				case IPTOS_MINCOST:
-					sk->priority=SOPRI_BACKGROUND;
-					break;
-				default:
-					sk->priority=SOPRI_NORMAL;
-					break;
-			}
+			sk->priority = rt_tos2priority(val);
 			return 0;
 		case IP_TTL:
 			if(val<1||val>255)
@@ -212,40 +264,56 @@
 				return -ENOPROTOOPT;
 			sk->ip_hdrincl=val?1:0;
 			return 0;
-#ifdef CONFIG_IP_MULTICAST
+		case IP_PMTUDISC:
+			if (val<0 || val>2)
+				return -EINVAL;
+			sk->ip_pmtudisc = val;
+			return 0;
+		case IP_RECVERR:
+			if (sk->type==SOCK_STREAM)
+				return -ENOPROTOOPT;
+			lock_sock(sk);
+			if (sk->ip_recverr && !val) {
+				struct sk_buff *skb;
+				/* Drain queued errors */
+				while((skb=skb_dequeue(&sk->error_queue))!=NULL) {
+					IS_SKB(skb);
+					kfree_skb(skb, FREE_READ);
+				}
+			}
+			sk->ip_recverr = val?1:0;
+			release_sock(sk);
+			return 0;
 		case IP_MULTICAST_TTL: 
-		{
 			sk->ip_mc_ttl=(int)ucval;
 	                return 0;
-		}
 		case IP_MULTICAST_LOOP: 
-		{
 			if(ucval!=0 && ucval!=1)
 				 return -EINVAL;
 			sk->ip_mc_loop=(int)ucval;
 			return 0;
-		}
 		case IP_MULTICAST_IF: 
 		{
 			struct in_addr addr;
-			struct device *dev=NULL;
+			struct device *dev = NULL;
 			
 			/*
 			 *	Check the arguments are allowable
 			 */
-				
+
 			err = copy_from_user(&addr,optval,sizeof(addr));
 			if (err)
 				return -EFAULT; 
 
 			
+			
 			/*
 			 *	What address has been requested
 			 */
 			
-			if(addr.s_addr==INADDR_ANY)	/* Default */
+			if (addr.s_addr==INADDR_ANY)	/* Default */
 			{
-				sk->ip_mc_name[0]=0;
+				memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
 				return 0;
 			}
 			
@@ -253,7 +321,7 @@
 			 *	Find the device
 			 */
 			 
-			dev=ip_mc_find_devfor(addr.s_addr);
+			dev=ip_dev_find(addr.s_addr, NULL);
 						
 			/*
 			 *	Did we find one
@@ -261,11 +329,13 @@
 			 
 			if(dev) 
 			{
-				strcpy(sk->ip_mc_name,dev->name);
+				memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
+				strcpy(sk->ip_mc_name, dev->name);
 				return 0;
 			}
 			return -EADDRNOTAVAIL;
 		}
+
 		
 		case IP_ADD_MEMBERSHIP: 
 		{
@@ -289,26 +359,14 @@
 			 *	Get device for use later
 			 */
 
-			if(mreq.imr_interface.s_addr==INADDR_ANY) 
-			{
-				/*
-				 *	Not set so scan.
-				 */
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
-				{
-					dev=rt->rt_dev;
-					atomic_dec(&rt->rt_use);
-					ip_rt_put(rt);
-				}
-			}
-			else
-			{
-				/*
-				 *	Find a suitable device.
-				 */
-				
-				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
-			}
+			if (mreq.imr_interface.s_addr==INADDR_ANY) {
+				err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL);
+				if (err)
+					return err;
+				dev = rt->u.dst.dev;
+				ip_rt_put(rt);
+			} else
+				dev = ip_dev_find(mreq.imr_interface.s_addr, NULL);
 			
 			/*
 			 *	No device, no cookies.
@@ -342,20 +400,14 @@
 			 *	Get device for use later 
 			 */
  
-			if(mreq.imr_interface.s_addr==INADDR_ANY) 
-			{
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
-			        {
-					dev=rt->rt_dev;
-					atomic_dec(&rt->rt_use);
-					ip_rt_put(rt);
-				}
-			}
-			else 
-			{
-			
-				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
-			}
+			if (mreq.imr_interface.s_addr==INADDR_ANY) {
+				err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL);
+				if (err)
+					return err;
+				dev = rt->u.dst.dev;
+				ip_rt_put(rt);
+			} else
+				dev = ip_dev_find(mreq.imr_interface.s_addr, NULL);
 			
 			/*
 			 *	Did we find a suitable device.
@@ -370,7 +422,67 @@
 			 
 			return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
 		}
-#endif			
+
+		case IP_MULTICAST_IFN:
+		{
+			struct device *dev;
+			char *devname;
+			
+			err = getname(optval, &devname);
+			if (err)
+				return err;
+			if (!devname[0])
+			{
+				memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
+				putname(devname);
+				return 0;
+			}
+			dev = dev_get(devname);
+			putname(devname);
+			if (!dev)
+				return -ENODEV;
+
+			memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
+			strcpy(sk->ip_mc_name,dev->name);
+			return 0;
+		}
+		case IP_ADD_MEMBERSHIPN:
+		{
+			struct ip_mreqn mreq;
+			struct device *dev = NULL;
+			
+			/*
+			 *	Check the arguments.
+			 */
+
+			err = copy_from_user(&mreq,optval,sizeof(mreq));
+			if (err)
+				return -EFAULT; 
+			dev = dev_get(mreq.imr_interface);
+			if (!dev)
+				return -ENODEV;
+			return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
+		
+		case IP_DROP_MEMBERSHIPN: 
+		{
+			struct ip_mreqn mreq;
+			struct device *dev=NULL;
+
+			/*
+			 *	Check the arguments
+			 */
+			 
+			err = copy_from_user(&mreq,optval,sizeof(mreq));
+			if (err)
+				return -EFAULT; 
+
+			dev=dev_get(mreq.imr_interface);
+			if(!dev)
+				return -ENODEV;
+			 
+			return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
 #ifdef CONFIG_IP_FIREWALL
 		case IP_FW_INSERT_IN:
 		case IP_FW_INSERT_OUT:
@@ -421,7 +533,6 @@
 			err=ip_acct_ctl(optname, &tmp_fw,optlen);
 			return -err;	/* -0 is 0 after all */
 #endif
-		/* IP_OPTIONS and friends go here eventually */
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -435,9 +546,7 @@
 int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
 {
 	int val,err;
-#ifdef CONFIG_IP_MULTICAST
 	int len;
-#endif
 	
 	if(level!=SOL_IP)
 		return -EOPNOTSUPP;
@@ -453,47 +562,18 @@
 	{
 		case IP_OPTIONS:
 			{
-				unsigned char optbuf[sizeof(struct options)+40];
-				struct options * opt = (struct options*)optbuf;
-
+				unsigned char optbuf[sizeof(struct ip_options)+40];
+				struct ip_options * opt = (struct ip_options*)optbuf;
 				cli();
 				opt->optlen = 0;
 				if (sk->opt)
-					memcpy(optbuf, sk->opt, sizeof(struct options)+sk->opt->optlen);
+					memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen);
 				sti();
 				if (opt->optlen == 0) 
-				{
 					return put_user(0, optlen);
-				}
-/*
- * Now we should undo all the changes done by ip_options_compile().
- */
-				if (opt->srr) 
-				{
-					unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
-					memmove(optptr+7, optptr+3, optptr[1]-7);
-					memcpy(optptr+3, &opt->faddr, 4);
-				}
-				if (opt->rr_needaddr) 
-				{
-					unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
-					memset(&optptr[optptr[2]-1], 0, 4);
-					optptr[2] -= 4;
-				}
-				if (opt->ts) 
-				{
-					unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
-					if (opt->ts_needtime) 
-					{
-						memset(&optptr[optptr[2]-1], 0, 4);
-						optptr[2] -= 4;
-					}
-					if (opt->ts_needaddr) 
-					{
-						memset(&optptr[optptr[2]-1], 0, 4);
-						optptr[2] -= 4;
-					}
-				}
+
+				ip_options_undo(opt);
+
 				err = put_user(opt->optlen, optlen);
 				if (!err) 
 				{
@@ -503,6 +583,21 @@
 				return err; 
 			}
 			return 0;
+		case IP_LOCALADDR:
+			val = (sk->ip_cmsg_flags & 1) != 0;
+			return 0;
+		case IP_LOCALDEV:
+			val = (sk->ip_cmsg_flags & 2) != 0;
+			return 0;
+		case IP_RECVOPTS:
+			val = (sk->ip_cmsg_flags & 4) != 0;
+			return 0;
+		case IP_RETOPTS:
+			val = (sk->ip_cmsg_flags & 8) != 0;
+			return 0;
+		case IP_RECVDSTADDR:
+			val = (sk->ip_cmsg_flags & 0x10) != 0;
+			return 0;
 		case IP_TOS:
 			val=sk->ip_tos;
 			break;
@@ -512,7 +607,12 @@
 		case IP_HDRINCL:
 			val=sk->ip_hdrincl;
 			break;
-#ifdef CONFIG_IP_MULTICAST			
+		case IP_PMTUDISC:
+			val=sk->ip_pmtudisc;
+			return 0;
+		case IP_RECVERR:
+			val=sk->ip_recverr;
+			return 0;
 		case IP_MULTICAST_TTL:
 			val=sk->ip_mc_ttl;
 			break;
@@ -520,6 +620,7 @@
 			val=sk->ip_mc_loop;
 			break;
 		case IP_MULTICAST_IF:
+		case IP_MULTICAST_IFN:
   			len=strlen(sk->ip_mc_name);
   			err = put_user(len, optlen);
 			if (!err)
@@ -529,7 +630,6 @@
 				    err = -EFAULT;
 			}
 			return err;
-#endif
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -537,4 +637,6 @@
 	if (err) 
 		return err; 
 	return put_user(val,(int *) optval);
+
+	return(0);
 }

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