linux-old/net/ipv4/ip_sockglue.c
<<
>>
Prefs
   1/*
   2 * INET         An implementation of the TCP/IP protocol suite for the LINUX
   3 *              operating system.  INET is implemented using the  BSD Socket
   4 *              interface as the means of communication with the user level.
   5 *
   6 *              The IP to API glue.
   7 *              
   8 * Version:     $Id: ip_sockglue.c,v 1.41 1999/03/25 10:04:29 davem Exp $
   9 *
  10 * Authors:     see ip.c
  11 *
  12 * Fixes:
  13 *              Many            :       Split from ip.c , see ip.c for history.
  14 *              Martin Mares    :       TOS setting fixed.
  15 *              Alan Cox        :       Fixed a couple of oopses in Martin's 
  16 *                                      TOS tweaks.
  17 *              Mike McLagan    :       Routing by source
  18 */
  19
  20#include <linux/config.h>
  21#include <linux/types.h>
  22#include <linux/mm.h>
  23#include <linux/sched.h>
  24#include <linux/skbuff.h>
  25#include <linux/ip.h>
  26#include <linux/icmp.h>
  27#include <linux/netdevice.h>
  28#include <net/sock.h>
  29#include <net/ip.h>
  30#include <net/icmp.h>
  31#include <net/tcp.h>
  32#include <linux/tcp.h>
  33#include <linux/udp.h>
  34#include <linux/igmp.h>
  35#include <linux/firewall.h>
  36#include <linux/ip_fw.h>
  37#include <linux/route.h>
  38#include <linux/mroute.h>
  39#include <net/route.h>
  40#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
  41#include <net/transp_v6.h>
  42#endif
  43
  44#ifdef CONFIG_IP_MASQUERADE
  45#include <linux/ip_masq.h>
  46#endif
  47
  48#include <linux/errqueue.h>
  49#include <asm/uaccess.h>
  50
  51#define MAX(a,b) ((a)>(b)?(a):(b))
  52
  53#define IP_CMSG_PKTINFO         1
  54#define IP_CMSG_TTL             2
  55#define IP_CMSG_TOS             4
  56#define IP_CMSG_RECVOPTS        8
  57#define IP_CMSG_RETOPTS         16
  58
  59/*
  60 *      SOL_IP control messages.
  61 */
  62
  63static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
  64{
  65        struct in_pktinfo info;
  66        struct rtable *rt = (struct rtable *)skb->dst;
  67
  68        info.ipi_addr.s_addr = skb->nh.iph->daddr;
  69        if (rt) {
  70                info.ipi_ifindex = rt->rt_iif;
  71                info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
  72        } else {
  73                info.ipi_ifindex = 0;
  74                info.ipi_spec_dst.s_addr = 0;
  75        }
  76
  77        put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
  78}
  79
  80static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
  81{
  82        int ttl = skb->nh.iph->ttl;
  83        put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
  84}
  85
  86static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
  87{
  88        put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos);
  89}
  90
  91static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
  92{
  93        if (IPCB(skb)->opt.optlen == 0)
  94                return;
  95
  96        put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1);
  97}
  98
  99
 100void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
 101{
 102        unsigned char optbuf[sizeof(struct ip_options) + 40];
 103        struct ip_options * opt = (struct ip_options*)optbuf;
 104
 105        if (IPCB(skb)->opt.optlen == 0)
 106                return;
 107
 108        if (ip_options_echo(opt, skb)) {
 109                msg->msg_flags |= MSG_CTRUNC;
 110                return;
 111        }
 112        ip_options_undo(opt);
 113
 114        put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
 115}
 116
 117
 118void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 119{
 120        unsigned flags = skb->sk->ip_cmsg_flags;
 121
 122        /* Ordered by supposed usage frequency */
 123        if (flags & 1)
 124                ip_cmsg_recv_pktinfo(msg, skb);
 125        if ((flags>>=1) == 0)
 126                return;
 127
 128        if (flags & 1)
 129                ip_cmsg_recv_ttl(msg, skb);
 130        if ((flags>>=1) == 0)
 131                return;
 132
 133        if (flags & 1)
 134                ip_cmsg_recv_tos(msg, skb);
 135        if ((flags>>=1) == 0)
 136                return;
 137
 138        if (flags & 1)
 139                ip_cmsg_recv_opts(msg, skb);
 140        if ((flags>>=1) == 0)
 141                return;
 142
 143        if (flags & 1)
 144                ip_cmsg_recv_retopts(msg, skb);
 145}
 146
 147int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
 148{
 149        int err;
 150        struct cmsghdr *cmsg;
 151
 152        for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
 153                if ((unsigned long)(((char*)cmsg - (char*)msg->msg_control)
 154                                    + cmsg->cmsg_len) > msg->msg_controllen) {
 155                        return -EINVAL;
 156                }
 157                if (cmsg->cmsg_level != SOL_IP)
 158                        continue;
 159                switch (cmsg->cmsg_type) {
 160                case IP_RETOPTS:
 161                        err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
 162                        err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40, 0);
 163                        if (err)
 164                                return err;
 165                        break;
 166                case IP_PKTINFO:
 167                {
 168                        struct in_pktinfo *info;
 169                        if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
 170                                return -EINVAL;
 171                        info = (struct in_pktinfo *)CMSG_DATA(cmsg);
 172                        ipc->oif = info->ipi_ifindex;
 173                        ipc->addr = info->ipi_spec_dst.s_addr;
 174                        break;
 175                }
 176                default:
 177                        return -EINVAL;
 178                }
 179        }
 180        return 0;
 181}
 182
 183
 184/* Special input handler for packets catched by router alert option.
 185   They are selected only by protocol field, and then processed likely
 186   local ones; but only if someone wants them! Otherwise, router
 187   not running rsvpd will kill RSVP.
 188
 189   It is user level problem, what it will make with them.
 190   I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
 191   but receiver should be enough clever f.e. to forward mtrace requests,
 192   sent to multicast group to reach destination designated router.
 193 */
 194struct ip_ra_chain *ip_ra_chain;
 195
 196int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *))
 197{
 198        struct ip_ra_chain *ra, *new_ra, **rap;
 199
 200        if (sk->type != SOCK_RAW || sk->num == IPPROTO_RAW)
 201                return -EINVAL;
 202
 203        new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
 204
 205        for (rap = &ip_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
 206                if (ra->sk == sk) {
 207                        if (on) {
 208                                if (new_ra)
 209                                        kfree(new_ra);
 210                                return -EADDRINUSE;
 211                        }
 212                        *rap = ra->next;
 213                        synchronize_bh();
 214
 215                        if (ra->destructor)
 216                                ra->destructor(sk);
 217                        kfree(ra);
 218                        return 0;
 219                }
 220        }
 221        if (new_ra == NULL)
 222                return -ENOBUFS;
 223        new_ra->sk = sk;
 224        new_ra->destructor = destructor;
 225
 226        new_ra->next = ra;
 227        wmb();
 228        *rap = new_ra;
 229
 230        return 0;
 231}
 232
 233void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, 
 234                   u16 port, u32 info, u8 *payload)
 235{
 236        struct sock_exterr_skb *serr;
 237
 238        if (!sk->ip_recverr)
 239                return;
 240
 241        skb = skb_clone(skb, GFP_ATOMIC);
 242        if (!skb)
 243                return;
 244
 245        serr = SKB_EXT_ERR(skb);  
 246        serr->ee.ee_errno = err;
 247        serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
 248        serr->ee.ee_type = skb->h.icmph->type; 
 249        serr->ee.ee_code = skb->h.icmph->code;
 250        serr->ee.ee_pad = 0;
 251        serr->ee.ee_info = info;
 252        serr->ee.ee_data = 0;
 253        serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb->nh.raw;
 254        serr->port = port;
 255
 256        skb->h.raw = payload;
 257        skb_pull(skb, payload - skb->data);
 258
 259        if (sock_queue_err_skb(sk, skb))
 260                kfree_skb(skb);
 261}
 262
 263void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info)
 264{
 265        struct sock_exterr_skb *serr;
 266        struct iphdr *iph;
 267        struct sk_buff *skb;
 268
 269        if (!sk->ip_recverr)
 270                return;
 271
 272        skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
 273        if (!skb)
 274                return;
 275
 276        iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr));
 277        skb->nh.iph = iph;
 278        iph->daddr = daddr;
 279
 280        serr = SKB_EXT_ERR(skb);  
 281        serr->ee.ee_errno = err;
 282        serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
 283        serr->ee.ee_type = 0; 
 284        serr->ee.ee_code = 0;
 285        serr->ee.ee_pad = 0;
 286        serr->ee.ee_info = info;
 287        serr->ee.ee_data = 0;
 288        serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
 289        serr->port = port;
 290
 291        skb->h.raw = skb->tail;
 292        skb_pull(skb, skb->tail - skb->data);
 293
 294        if (sock_queue_err_skb(sk, skb))
 295                kfree_skb(skb);
 296}
 297
 298/* 
 299 *      Handle MSG_ERRQUEUE
 300 */
 301int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
 302{
 303        struct sock_exterr_skb *serr;
 304        struct sk_buff *skb, *skb2;
 305        struct sockaddr_in *sin;
 306        struct {
 307                struct sock_extended_err ee;
 308                struct sockaddr_in       offender;
 309        } errhdr;
 310        int err;
 311        int copied;
 312
 313        err = -EAGAIN;
 314        skb = skb_dequeue(&sk->error_queue);
 315        if (skb == NULL)
 316                goto out;
 317
 318        copied = skb->len;
 319        if (copied > len) {
 320                msg->msg_flags |= MSG_TRUNC;
 321                copied = len;
 322        }
 323        err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
 324        if (err)
 325                goto out_free_skb;
 326
 327        serr = SKB_EXT_ERR(skb);
 328
 329        sin = (struct sockaddr_in *)msg->msg_name;
 330        if (sin) {
 331                sin->sin_family = AF_INET; 
 332                sin->sin_addr.s_addr = *(u32*)(skb->nh.raw + serr->addr_offset);
 333                sin->sin_port = serr->port; 
 334        }
 335
 336        memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
 337        sin = &errhdr.offender;
 338        sin->sin_family = AF_UNSPEC;
 339        if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
 340                sin->sin_family = AF_INET;
 341                sin->sin_addr.s_addr = skb->nh.iph->saddr;
 342                if (sk->ip_cmsg_flags)
 343                        ip_cmsg_recv(msg, skb);
 344        }
 345
 346        put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
 347
 348        /* Now we could try to dump offended packet options */
 349
 350        msg->msg_flags |= MSG_ERRQUEUE;
 351        err = copied;
 352
 353        /* Reset and regenerate socket error */
 354        sk->err = 0;
 355        if ((skb2 = skb_peek(&sk->error_queue)) != NULL) {
 356                sk->err = SKB_EXT_ERR(skb2)->ee.ee_errno;
 357                sk->error_report(sk);
 358        }
 359
 360out_free_skb:   
 361        kfree_skb(skb);
 362out:
 363        return err;
 364}
 365
 366
 367/*
 368 *      Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
 369 *      an IP socket.
 370 *
 371 *      We implement IP_TOS (type of service), IP_TTL (time to live).
 372 */
 373
 374int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
 375{
 376        int val=0,err;
 377#if defined(CONFIG_IP_FIREWALL)
 378        char tmp_fw[MAX(sizeof(struct ip_fwtest),sizeof(struct ip_fwnew))];
 379#endif
 380        if(optlen>=sizeof(int)) {
 381                if(get_user(val, (int *) optval))
 382                        return -EFAULT;
 383        } else if(optlen>=sizeof(char)) {
 384                unsigned char ucval;
 385                if(get_user(ucval, (unsigned char *) optval))
 386                        return -EFAULT;
 387                val = (int)ucval;
 388        }
 389        /* If optlen==0, it is equivalent to val == 0 */
 390        
 391        if(level!=SOL_IP)
 392                return -ENOPROTOOPT;
 393#ifdef CONFIG_IP_MROUTE
 394        if(optname>=MRT_BASE && optname <=MRT_BASE+10)
 395        {
 396                return ip_mroute_setsockopt(sk,optname,optval,optlen);
 397        }
 398#endif
 399        
 400        switch(optname)
 401        {
 402                case IP_OPTIONS:
 403                {
 404                        struct ip_options * opt = NULL;
 405                        if (optlen > 40 || optlen < 0)
 406                                return -EINVAL;
 407                        err = ip_options_get(&opt, optval, optlen, 1);
 408                        if (err)
 409                                return err;
 410                        lock_sock(sk);
 411                        if (sk->type == SOCK_STREAM) {
 412                                struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 413#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 414                                if (sk->family == PF_INET ||
 415                                    ((tcp_connected(sk->state) || sk->state == TCP_SYN_SENT)
 416                                     && sk->daddr != LOOPBACK4_IPV6)) {
 417#endif
 418                                        if (opt)
 419                                                tp->ext_header_len = opt->optlen;
 420                                        tcp_sync_mss(sk, tp->pmtu_cookie);
 421#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 422                                }
 423#endif
 424                        }
 425                        opt = xchg(&sk->opt, opt);
 426                        release_sock(sk);
 427                        if (opt)
 428                                kfree_s(opt, sizeof(struct ip_options) + opt->optlen);
 429                        return 0;
 430                }
 431                case IP_PKTINFO:
 432                        if (val)
 433                                sk->ip_cmsg_flags |= IP_CMSG_PKTINFO;
 434                        else
 435                                sk->ip_cmsg_flags &= ~IP_CMSG_PKTINFO;
 436                        return 0;
 437                case IP_RECVTTL:
 438                        if (val)
 439                                sk->ip_cmsg_flags |=  IP_CMSG_TTL;
 440                        else
 441                                sk->ip_cmsg_flags &= ~IP_CMSG_TTL;
 442                        return 0;
 443                case IP_RECVTOS:
 444                        if (val)
 445                                sk->ip_cmsg_flags |=  IP_CMSG_TOS;
 446                        else
 447                                sk->ip_cmsg_flags &= ~IP_CMSG_TOS;
 448                        return 0;
 449                case IP_RECVOPTS:
 450                        if (val)
 451                                sk->ip_cmsg_flags |=  IP_CMSG_RECVOPTS;
 452                        else
 453                                sk->ip_cmsg_flags &= ~IP_CMSG_RECVOPTS;
 454                        return 0;
 455                case IP_RETOPTS:
 456                        if (val)
 457                                sk->ip_cmsg_flags |= IP_CMSG_RETOPTS;
 458                        else
 459                                sk->ip_cmsg_flags &= ~IP_CMSG_RETOPTS;
 460                        return 0;
 461                case IP_TOS:    /* This sets both TOS and Precedence */
 462                          /* Reject setting of unused bits */
 463                        if (val & ~(IPTOS_TOS_MASK|IPTOS_PREC_MASK))
 464                                return -EINVAL;
 465                        if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && 
 466                            !capable(CAP_NET_ADMIN))
 467                                return -EPERM;
 468                        if (sk->ip_tos != val) {
 469                                lock_sock(sk);
 470                                sk->ip_tos=val;
 471                                sk->priority = rt_tos2priority(val);
 472                                dst_release(xchg(&sk->dst_cache, NULL)); 
 473                                release_sock(sk);
 474                        }
 475                        return 0;
 476                case IP_TTL:
 477                        if (optlen<1)
 478                                return -EINVAL;
 479                        if(val==-1)
 480                                val = ip_statistics.IpDefaultTTL;
 481                        if(val<1||val>255)
 482                                return -EINVAL;
 483                        sk->ip_ttl=val;
 484                        return 0;
 485                case IP_HDRINCL:
 486                        if(sk->type!=SOCK_RAW)
 487                                return -ENOPROTOOPT;
 488                        sk->ip_hdrincl=val?1:0;
 489                        return 0;
 490                case IP_MTU_DISCOVER:
 491                        if (val<0 || val>2)
 492                                return -EINVAL;
 493                        sk->ip_pmtudisc = val;
 494                        return 0;
 495                case IP_RECVERR:
 496                        sk->ip_recverr = !!val;
 497                        if (!val)
 498                                skb_queue_purge(&sk->error_queue);
 499                        return 0;
 500                case IP_MULTICAST_TTL: 
 501                        if (optlen<1)
 502                                return -EINVAL;
 503                        if (val==-1)
 504                                val = 1;
 505                        if (val < 0 || val > 255)
 506                                return -EINVAL;
 507                        sk->ip_mc_ttl=val;
 508                        return 0;
 509                case IP_MULTICAST_LOOP: 
 510                        if (optlen<1)
 511                                return -EINVAL;
 512                        sk->ip_mc_loop = val ? 1 : 0;
 513                        return 0;
 514                case IP_MULTICAST_IF: 
 515                {
 516                        struct ip_mreqn mreq;
 517                        struct device *dev = NULL;
 518                        
 519                        /*
 520                         *      Check the arguments are allowable
 521                         */
 522
 523                        if (optlen >= sizeof(struct ip_mreqn)) {
 524                                if (copy_from_user(&mreq,optval,sizeof(mreq)))
 525                                        return -EFAULT;
 526                        } else {
 527                                memset(&mreq, 0, sizeof(mreq));
 528                                if (optlen >= sizeof(struct in_addr) &&
 529                                    copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr)))
 530                                        return -EFAULT;
 531                        }
 532
 533                        if (!mreq.imr_ifindex) {
 534                                if (mreq.imr_address.s_addr == INADDR_ANY) {
 535                                        sk->ip_mc_index = 0;
 536                                        sk->ip_mc_addr  = 0;
 537                                        return 0;
 538                                }
 539                                dev = ip_dev_find(mreq.imr_address.s_addr);
 540                        } else
 541                                dev = dev_get_by_index(mreq.imr_ifindex);
 542
 543                        if (!dev)
 544                                return -EADDRNOTAVAIL;
 545
 546                        if (sk->bound_dev_if && dev->ifindex != sk->bound_dev_if)
 547                                return -EINVAL;
 548
 549                        sk->ip_mc_index = mreq.imr_ifindex;
 550                        sk->ip_mc_addr  = mreq.imr_address.s_addr;
 551                        return 0;
 552                }
 553
 554                case IP_ADD_MEMBERSHIP:
 555                case IP_DROP_MEMBERSHIP: 
 556                {
 557                        struct ip_mreqn mreq;
 558                        
 559                        if (optlen < sizeof(struct ip_mreq))
 560                                return -EINVAL;
 561                        if (optlen >= sizeof(struct ip_mreqn)) {
 562                                if(copy_from_user(&mreq,optval,sizeof(mreq)))
 563                                        return -EFAULT;
 564                        } else {
 565                                memset(&mreq, 0, sizeof(mreq));
 566                                if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
 567                                        return -EFAULT; 
 568                        }
 569
 570                        if (optname == IP_ADD_MEMBERSHIP)
 571                                return ip_mc_join_group(sk,&mreq);
 572                        else
 573                                return ip_mc_leave_group(sk,&mreq);
 574                }
 575                case IP_ROUTER_ALERT:   
 576                        return ip_ra_control(sk, val ? 1 : 0, NULL);
 577                
 578#ifdef CONFIG_IP_FIREWALL
 579                case IP_FW_MASQ_TIMEOUTS:
 580                case IP_FW_APPEND:
 581                case IP_FW_REPLACE:
 582                case IP_FW_DELETE:
 583                case IP_FW_DELETE_NUM:
 584                case IP_FW_INSERT:
 585                case IP_FW_FLUSH:
 586                case IP_FW_ZERO:
 587                case IP_FW_CHECK:
 588                case IP_FW_CREATECHAIN:
 589                case IP_FW_DELETECHAIN:
 590                case IP_FW_POLICY:
 591                        if(!capable(CAP_NET_ADMIN))
 592                                return -EACCES;
 593                        if(optlen>sizeof(tmp_fw) || optlen<1)
 594                                return -EINVAL;
 595                        if(copy_from_user(&tmp_fw,optval,optlen))
 596                                return -EFAULT;
 597                        err=ip_fw_ctl(optname, &tmp_fw,optlen);
 598                        return -err;    /* -0 is 0 after all */
 599#endif /* CONFIG_IP_FIREWALL */
 600#ifdef CONFIG_IP_MASQUERADE
 601                case IP_FW_MASQ_CTL:
 602                        if(!capable(CAP_NET_ADMIN))
 603                                return -EPERM;
 604                        if(optlen<1)
 605                                return -EINVAL;
 606                        err=ip_masq_uctl(optname, optval ,optlen);
 607                        return err;
 608                        
 609#endif
 610                default:
 611                        return(-ENOPROTOOPT);
 612        }
 613}
 614
 615/*
 616 *      Get the options. Note for future reference. The GET of IP options gets the
 617 *      _received_ ones. The set sets the _sent_ ones.
 618 */
 619
 620int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
 621{
 622        int val;
 623        int len;
 624        
 625        if(level!=SOL_IP)
 626                return -EOPNOTSUPP;
 627
 628#ifdef CONFIG_IP_MROUTE
 629        if(optname>=MRT_BASE && optname <=MRT_BASE+10)
 630        {
 631                return ip_mroute_getsockopt(sk,optname,optval,optlen);
 632        }
 633#endif
 634
 635        if(get_user(len,optlen))
 636                return -EFAULT;
 637
 638        switch(optname)
 639        {
 640                case IP_OPTIONS:
 641                        {
 642                                unsigned char optbuf[sizeof(struct ip_options)+40];
 643                                struct ip_options * opt = (struct ip_options*)optbuf;
 644                                lock_sock(sk);
 645                                opt->optlen = 0;
 646                                if (sk->opt)
 647                                        memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen);
 648                                release_sock(sk);
 649                                if (opt->optlen == 0) 
 650                                        return put_user(0, optlen);
 651
 652                                ip_options_undo(opt);
 653
 654                                len=min(len, opt->optlen);
 655                                if(put_user(len, optlen))
 656                                        return -EFAULT;
 657                                if(copy_to_user(optval, opt->__data, len))
 658                                        return -EFAULT;
 659                                return 0;
 660                        }
 661                case IP_PKTINFO:
 662                        val = (sk->ip_cmsg_flags & IP_CMSG_PKTINFO) != 0;
 663                        break;
 664                case IP_RECVTTL:
 665                        val = (sk->ip_cmsg_flags & IP_CMSG_TTL) != 0;
 666                        break;
 667                case IP_RECVTOS:
 668                        val = (sk->ip_cmsg_flags & IP_CMSG_TOS) != 0;
 669                        break;
 670                case IP_RECVOPTS:
 671                        val = (sk->ip_cmsg_flags & IP_CMSG_RECVOPTS) != 0;
 672                        break;
 673                case IP_RETOPTS:
 674                        val = (sk->ip_cmsg_flags & IP_CMSG_RETOPTS) != 0;
 675                        break;
 676                case IP_TOS:
 677                        val=sk->ip_tos;
 678                        break;
 679                case IP_TTL:
 680                        val=sk->ip_ttl;
 681                        break;
 682                case IP_HDRINCL:
 683                        val=sk->ip_hdrincl;
 684                        break;
 685                case IP_MTU_DISCOVER:
 686                        val=sk->ip_pmtudisc;
 687                        break;
 688                case IP_MTU:
 689                        val = 0;        
 690                        lock_sock(sk);
 691                        if (sk->dst_cache)              
 692                                val = sk->dst_cache->pmtu;
 693                        release_sock(sk);
 694                        if (!val)
 695                                return -ENOTCONN;
 696                        break;
 697                case IP_RECVERR:
 698                        val=sk->ip_recverr;
 699                        break;
 700                case IP_MULTICAST_TTL:
 701                        val=sk->ip_mc_ttl;
 702                        break;
 703                case IP_MULTICAST_LOOP:
 704                        val=sk->ip_mc_loop;
 705                        break;
 706                case IP_MULTICAST_IF:
 707                {
 708                        struct ip_mreqn mreq;
 709                        len = min(len,sizeof(struct ip_mreqn));
 710                        if(put_user(len, optlen))
 711                                return -EFAULT;
 712                        mreq.imr_ifindex = sk->ip_mc_index;
 713                        mreq.imr_address.s_addr = sk->ip_mc_addr;
 714                        mreq.imr_multiaddr.s_addr = 0;
 715                        if(copy_to_user((void *)optval, &mreq, len))
 716                                return -EFAULT;
 717                        return 0;
 718                }
 719                default:
 720                        return(-ENOPROTOOPT);
 721        }
 722        
 723        if (len < sizeof(int) && len > 0 && val>=0 && val<255) {
 724                unsigned char ucval = (unsigned char)val;
 725                len = 1;
 726                if(put_user(len, optlen))
 727                        return -EFAULT;
 728                if(copy_to_user(optval,&ucval,1))
 729                        return -EFAULT;
 730        } else {
 731                len=min(sizeof(int),len);
 732                if(put_user(len, optlen))
 733                        return -EFAULT;
 734                if(copy_to_user(optval,&val,len))
 735                        return -EFAULT;
 736        }
 737        return 0;
 738}
 739
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.