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
- Lines: 645
- Date:
Thu Dec 12 16:54:24 1996
- Orig file:
v2.1.14/linux/net/ipv4/ip_sockglue.c
- Orig date:
Tue Nov 19 15:54:01 1996
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