Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

ipv6/datagram.c

Go to the documentation of this file.
00001 /*
00002  *      common UDP/RAW code
00003  *      Linux INET6 implementation 
00004  *
00005  *      Authors:
00006  *      Pedro Roque             <roque@di.fc.ul.pt>     
00007  *
00008  *      $Id: datagram.c,v 1.17 1999/04/22 10:07:40 davem Exp $
00009  *
00010  *      This program is free software; you can redistribute it and/or
00011  *      modify it under the terms of the GNU General Public License
00012  *      as published by the Free Software Foundation; either version
00013  *      2 of the License, or (at your option) any later version.
00014  */
00015 
00016 #include <linux/errno.h>
00017 #include <linux/types.h>
00018 #include <linux/kernel.h>
00019 #include <linux/sched.h>
00020 #include <linux/interrupt.h>
00021 #include <linux/socket.h>
00022 #include <linux/sockios.h>
00023 #include <linux/in6.h>
00024 #include <linux/ipv6.h>
00025 #include <linux/route.h>
00026 
00027 #include <net/ipv6.h>
00028 #include <net/ndisc.h>
00029 #include <net/addrconf.h>
00030 #include <net/transp_v6.h>
00031 
00032 #include <linux/errqueue.h>
00033 #include <asm/uaccess.h>
00034 
00035 void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 
00036                      u16 port, u32 info, u8 *payload)
00037 {
00038         struct icmp6hdr *icmph = (struct icmp6hdr *)skb->h.raw;
00039         struct sock_exterr_skb *serr;
00040 
00041         if (!sk->net_pinfo.af_inet6.recverr)
00042                 return;
00043 
00044         skb = skb_clone(skb, GFP_ATOMIC);
00045         if (!skb)
00046                 return;
00047 
00048         serr = SKB_EXT_ERR(skb);
00049         serr->ee.ee_errno = err;
00050         serr->ee.ee_origin = SO_EE_ORIGIN_ICMP6;
00051         serr->ee.ee_type = icmph->icmp6_type; 
00052         serr->ee.ee_code = icmph->icmp6_code;
00053         serr->ee.ee_pad = 0;
00054         serr->ee.ee_info = info;
00055         serr->ee.ee_data = 0;
00056         serr->addr_offset = (u8*)&(((struct ipv6hdr*)(icmph+1))->daddr) - skb->nh.raw;
00057         serr->port = port;
00058 
00059         skb->h.raw = payload;
00060         skb_pull(skb, payload - skb->data);
00061 
00062         if (sock_queue_err_skb(sk, skb))
00063                 kfree_skb(skb);
00064 }
00065 
00066 void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info)
00067 {
00068         struct sock_exterr_skb *serr;
00069         struct ipv6hdr *iph;
00070         struct sk_buff *skb;
00071 
00072         if (!sk->net_pinfo.af_inet6.recverr)
00073                 return;
00074 
00075         skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
00076         if (!skb)
00077                 return;
00078 
00079         iph = (struct ipv6hdr*)skb_put(skb, sizeof(struct ipv6hdr));
00080         skb->nh.ipv6h = iph;
00081         memcpy(&iph->daddr, fl->fl6_dst, 16);
00082 
00083         serr = SKB_EXT_ERR(skb);
00084         serr->ee.ee_errno = err;
00085         serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
00086         serr->ee.ee_type = 0; 
00087         serr->ee.ee_code = 0;
00088         serr->ee.ee_pad = 0;
00089         serr->ee.ee_info = info;
00090         serr->ee.ee_data = 0;
00091         serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
00092         serr->port = fl->uli_u.ports.dport;
00093 
00094         skb->h.raw = skb->tail;
00095         skb_pull(skb, skb->tail - skb->data);
00096 
00097         if (sock_queue_err_skb(sk, skb))
00098                 kfree_skb(skb);
00099 }
00100 
00101 /* 
00102  *      Handle MSG_ERRQUEUE
00103  */
00104 int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
00105 {
00106         struct sock_exterr_skb *serr;
00107         struct sk_buff *skb, *skb2;
00108         struct sockaddr_in6 *sin;
00109         struct {
00110                 struct sock_extended_err ee;
00111                 struct sockaddr_in6      offender;
00112         } errhdr;
00113         int err;
00114         int copied;
00115 
00116         err = -EAGAIN;
00117         skb = skb_dequeue(&sk->error_queue);
00118         if (skb == NULL)
00119                 goto out;
00120 
00121         copied = skb->len;
00122         if (copied > len) {
00123                 msg->msg_flags |= MSG_TRUNC;
00124                 copied = len;
00125         }
00126         err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
00127         if (err)
00128                 goto out_free_skb;
00129 
00130         serr = SKB_EXT_ERR(skb);
00131 
00132         sin = (struct sockaddr_in6 *)msg->msg_name;
00133         if (sin) {
00134                 sin->sin6_family = AF_INET6;
00135                 sin->sin6_flowinfo = 0;
00136                 sin->sin6_port = serr->port; 
00137                 if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
00138                         memcpy(&sin->sin6_addr, skb->nh.raw + serr->addr_offset, 16);
00139                         if (sk->net_pinfo.af_inet6.sndflow)
00140                                 sin->sin6_flowinfo = *(u32*)(skb->nh.raw + serr->addr_offset - 24) & IPV6_FLOWINFO_MASK;
00141                 } else
00142                         ipv6_addr_set(&sin->sin6_addr, 0, 0,
00143                                       __constant_htonl(0xffff),
00144                                       *(u32*)(skb->nh.raw + serr->addr_offset));
00145         }
00146 
00147         memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
00148         sin = &errhdr.offender;
00149         sin->sin6_family = AF_UNSPEC;
00150         if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
00151                 sin->sin6_family = AF_INET6;
00152                 sin->sin6_flowinfo = 0;
00153                 if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
00154                         memcpy(&sin->sin6_addr, &skb->nh.ipv6h->saddr, 16);
00155                         if (sk->net_pinfo.af_inet6.rxopt.all)
00156                                 datagram_recv_ctl(sk, msg, skb);
00157                 } else {
00158                         ipv6_addr_set(&sin->sin6_addr, 0, 0,
00159                                       __constant_htonl(0xffff),
00160                                       skb->nh.iph->saddr);
00161                         if (sk->ip_cmsg_flags)
00162                                 ip_cmsg_recv(msg, skb);
00163                 }
00164         }
00165 
00166         put_cmsg(msg, SOL_IPV6, IPV6_RECVERR, sizeof(errhdr), &errhdr);
00167 
00168         /* Now we could try to dump offended packet options */
00169 
00170         msg->msg_flags |= MSG_ERRQUEUE;
00171         err = copied;
00172 
00173         /* Reset and regenerate socket error */
00174         sk->err = 0;
00175         if ((skb2 = skb_peek(&sk->error_queue)) != NULL) {
00176                 sk->err = SKB_EXT_ERR(skb2)->ee.ee_errno;
00177                 sk->error_report(sk);
00178         }
00179 
00180 out_free_skb:   
00181         kfree_skb(skb);
00182 out:
00183         return err;
00184 }
00185 
00186 
00187 
00188 int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
00189 {
00190         struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
00191         struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
00192 
00193         if (np->rxopt.bits.rxinfo) {
00194                 struct in6_pktinfo src_info;
00195 
00196                 src_info.ipi6_ifindex = opt->iif;
00197                 ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
00198                 put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
00199         }
00200 
00201         if (np->rxopt.bits.rxhlim) {
00202                 int hlim = skb->nh.ipv6h->hop_limit;
00203                 put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
00204         }
00205 
00206         if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) {
00207                 u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
00208                 put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
00209         }
00210         if (np->rxopt.bits.hopopts && opt->hop) {
00211                 u8 *ptr = skb->nh.raw + opt->hop;
00212                 put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
00213         }
00214         if (np->rxopt.bits.dstopts && opt->dst0) {
00215                 u8 *ptr = skb->nh.raw + opt->dst0;
00216                 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
00217         }
00218         if (np->rxopt.bits.srcrt && opt->srcrt) {
00219                 struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
00220                 put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
00221         }
00222         if (np->rxopt.bits.authhdr && opt->auth) {
00223                 u8 *ptr = skb->nh.raw + opt->auth;
00224                 put_cmsg(msg, SOL_IPV6, IPV6_AUTHHDR, (ptr[1]+1)<<2, ptr);
00225         }
00226         if (np->rxopt.bits.dstopts && opt->dst1) {
00227                 u8 *ptr = skb->nh.raw + opt->dst1;
00228                 put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
00229         }
00230         return 0;
00231 }
00232 
00233 int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
00234                       struct ipv6_txoptions *opt,
00235                       int *hlimit)
00236 {
00237         struct in6_pktinfo *src_info;
00238         struct cmsghdr *cmsg;
00239         struct ipv6_rt_hdr *rthdr;
00240         struct ipv6_opt_hdr *hdr;
00241         int len;
00242         int err = 0;
00243 
00244         for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
00245 
00246                 if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
00247                     (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
00248                                     + cmsg->cmsg_len) > msg->msg_controllen) {
00249                         err = -EINVAL;
00250                         goto exit_f;
00251                 }
00252 
00253                 if (cmsg->cmsg_level != SOL_IPV6)
00254                         continue;
00255 
00256                 switch (cmsg->cmsg_type) {
00257                 case IPV6_PKTINFO:
00258                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
00259                                 err = -EINVAL;
00260                                 goto exit_f;
00261                         }
00262 
00263                         src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
00264                         
00265                         if (src_info->ipi6_ifindex) {
00266                                 if (fl->oif && src_info->ipi6_ifindex != fl->oif)
00267                                         return -EINVAL;
00268                                 fl->oif = src_info->ipi6_ifindex;
00269                         }
00270 
00271                         if (!ipv6_addr_any(&src_info->ipi6_addr)) {
00272                                 struct inet6_ifaddr *ifp;
00273 
00274                                 ifp = ipv6_chk_addr(&src_info->ipi6_addr, NULL, 0);
00275 
00276                                 if (ifp == NULL) {
00277                                         err = -EINVAL;
00278                                         goto exit_f;
00279                                 }
00280 
00281                                 fl->fl6_src = &src_info->ipi6_addr;
00282                         }
00283 
00284                         break;
00285 
00286                 case IPV6_FLOWINFO:
00287                         if (cmsg->cmsg_len < CMSG_LEN(4)) {
00288                                 err = -EINVAL;
00289                                 goto exit_f;
00290                         }
00291 
00292                         if (fl->fl6_flowlabel&IPV6_FLOWINFO_MASK) {
00293                                 if ((fl->fl6_flowlabel^*(u32 *)CMSG_DATA(cmsg))&~IPV6_FLOWINFO_MASK) {
00294                                         err = -EINVAL;
00295                                         goto exit_f;
00296                                 }
00297                         }
00298                         fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
00299                         break;
00300 
00301                 case IPV6_HOPOPTS:
00302                         if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
00303                                 err = -EINVAL;
00304                                 goto exit_f;
00305                         }
00306 
00307                         hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
00308                         len = ((hdr->hdrlen + 1) << 3);
00309                         if (cmsg->cmsg_len < CMSG_LEN(len)) {
00310                                 err = -EINVAL;
00311                                 goto exit_f;
00312                         }
00313                         if (!capable(CAP_NET_RAW)) {
00314                                 err = -EPERM;
00315                                 goto exit_f;
00316                         }
00317                         opt->opt_nflen += len;
00318                         opt->hopopt = hdr;
00319                         break;
00320 
00321                 case IPV6_DSTOPTS:
00322                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
00323                                 err = -EINVAL;
00324                                 goto exit_f;
00325                         }
00326 
00327                         hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
00328                         len = ((hdr->hdrlen + 1) << 3);
00329                         if (cmsg->cmsg_len < CMSG_LEN(len)) {
00330                                 err = -EINVAL;
00331                                 goto exit_f;
00332                         }
00333                         if (!capable(CAP_NET_RAW)) {
00334                                 err = -EPERM;
00335                                 goto exit_f;
00336                         }
00337                         if (opt->dst1opt) {
00338                                 err = -EINVAL;
00339                                 goto exit_f;
00340                         }
00341                         opt->opt_flen += len;
00342                         opt->dst1opt = hdr;
00343                         break;
00344 
00345                 case IPV6_AUTHHDR:
00346                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
00347                                 err = -EINVAL;
00348                                 goto exit_f;
00349                         }
00350 
00351                         hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
00352                         len = ((hdr->hdrlen + 2) << 2);
00353                         if (cmsg->cmsg_len < CMSG_LEN(len)) {
00354                                 err = -EINVAL;
00355                                 goto exit_f;
00356                         }
00357                         if (len & ~7) {
00358                                 err = -EINVAL;
00359                                 goto exit_f;
00360                         }
00361                         opt->opt_flen += len;
00362                         opt->auth = hdr;
00363                         break;
00364 
00365                 case IPV6_RTHDR:
00366                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
00367                                 err = -EINVAL;
00368                                 goto exit_f;
00369                         }
00370 
00371                         rthdr = (struct ipv6_rt_hdr *)CMSG_DATA(cmsg);
00372 
00373                         /*
00374                          *      TYPE 0
00375                          */
00376                         if (rthdr->type) {
00377                                 err = -EINVAL;
00378                                 goto exit_f;
00379                         }
00380 
00381                         len = ((rthdr->hdrlen + 1) << 3);
00382 
00383                         if (cmsg->cmsg_len < CMSG_LEN(len)) {
00384                                 err = -EINVAL;
00385                                 goto exit_f;
00386                         }
00387 
00388                         /* segments left must also match */
00389                         if ((rthdr->hdrlen >> 1) != rthdr->segments_left) {
00390                                 err = -EINVAL;
00391                                 goto exit_f;
00392                         }
00393 
00394                         opt->opt_nflen += len;
00395                         opt->srcrt = rthdr;
00396 
00397                         if (opt->dst1opt) {
00398                                 int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
00399 
00400                                 opt->opt_nflen += dsthdrlen;
00401                                 opt->dst0opt = opt->dst1opt;
00402                                 opt->dst1opt = NULL;
00403                                 opt->opt_flen -= dsthdrlen;
00404                         }
00405 
00406                         break;
00407 
00408                 case IPV6_HOPLIMIT:
00409                         if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
00410                                 err = -EINVAL;
00411                                 goto exit_f;
00412                         }
00413 
00414                         *hlimit = *(int *)CMSG_DATA(cmsg);
00415                         break;
00416 
00417                 default:
00418                         printk(KERN_DEBUG "invalid cmsg type: %d\n", cmsg->cmsg_type);
00419                         err = -EINVAL;
00420                         break;
00421                 };
00422         }
00423 
00424 exit_f:
00425         return err;
00426 }