00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
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
00169
00170 msg->msg_flags |= MSG_ERRQUEUE;
00171 err = copied;
00172
00173
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
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
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 }