diff options
Diffstat (limited to 'net/ipv4/ip_sockglue.c')
-rw-r--r-- | net/ipv4/ip_sockglue.c | 72 |
1 files changed, 55 insertions, 17 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 465abf0a9869..43c05854d752 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #define IP_CMSG_RECVOPTS 8 | 48 | #define IP_CMSG_RECVOPTS 8 |
49 | #define IP_CMSG_RETOPTS 16 | 49 | #define IP_CMSG_RETOPTS 16 |
50 | #define IP_CMSG_PASSSEC 32 | 50 | #define IP_CMSG_PASSSEC 32 |
51 | #define IP_CMSG_ORIGDSTADDR 64 | ||
51 | 52 | ||
52 | /* | 53 | /* |
53 | * SOL_IP control messages. | 54 | * SOL_IP control messages. |
@@ -94,7 +95,7 @@ static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) | |||
94 | static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) | 95 | static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) |
95 | { | 96 | { |
96 | unsigned char optbuf[sizeof(struct ip_options) + 40]; | 97 | unsigned char optbuf[sizeof(struct ip_options) + 40]; |
97 | struct ip_options * opt = (struct ip_options*)optbuf; | 98 | struct ip_options * opt = (struct ip_options *)optbuf; |
98 | 99 | ||
99 | if (IPCB(skb)->opt.optlen == 0) | 100 | if (IPCB(skb)->opt.optlen == 0) |
100 | return; | 101 | return; |
@@ -126,6 +127,27 @@ static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb) | |||
126 | security_release_secctx(secdata, seclen); | 127 | security_release_secctx(secdata, seclen); |
127 | } | 128 | } |
128 | 129 | ||
130 | static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) | ||
131 | { | ||
132 | struct sockaddr_in sin; | ||
133 | struct iphdr *iph = ip_hdr(skb); | ||
134 | __be16 *ports = (__be16 *)skb_transport_header(skb); | ||
135 | |||
136 | if (skb_transport_offset(skb) + 4 > skb->len) | ||
137 | return; | ||
138 | |||
139 | /* All current transport protocols have the port numbers in the | ||
140 | * first four bytes of the transport header and this function is | ||
141 | * written with this assumption in mind. | ||
142 | */ | ||
143 | |||
144 | sin.sin_family = AF_INET; | ||
145 | sin.sin_addr.s_addr = iph->daddr; | ||
146 | sin.sin_port = ports[1]; | ||
147 | memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); | ||
148 | |||
149 | put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin); | ||
150 | } | ||
129 | 151 | ||
130 | void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) | 152 | void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) |
131 | { | 153 | { |
@@ -160,6 +182,12 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) | |||
160 | 182 | ||
161 | if (flags & 1) | 183 | if (flags & 1) |
162 | ip_cmsg_recv_security(msg, skb); | 184 | ip_cmsg_recv_security(msg, skb); |
185 | |||
186 | if ((flags>>=1) == 0) | ||
187 | return; | ||
188 | if (flags & 1) | ||
189 | ip_cmsg_recv_dstaddr(msg, skb); | ||
190 | |||
163 | } | 191 | } |
164 | 192 | ||
165 | int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) | 193 | int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) |
@@ -411,7 +439,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
411 | int optname, char __user *optval, int optlen) | 439 | int optname, char __user *optval, int optlen) |
412 | { | 440 | { |
413 | struct inet_sock *inet = inet_sk(sk); | 441 | struct inet_sock *inet = inet_sk(sk); |
414 | int val=0,err; | 442 | int val = 0, err; |
415 | 443 | ||
416 | if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | | 444 | if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) | |
417 | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | | 445 | (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) | |
@@ -421,7 +449,8 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
421 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | | 449 | (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) | |
422 | (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT))) || | 450 | (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT))) || |
423 | optname == IP_MULTICAST_TTL || | 451 | optname == IP_MULTICAST_TTL || |
424 | optname == IP_MULTICAST_LOOP) { | 452 | optname == IP_MULTICAST_LOOP || |
453 | optname == IP_RECVORIGDSTADDR) { | ||
425 | if (optlen >= sizeof(int)) { | 454 | if (optlen >= sizeof(int)) { |
426 | if (get_user(val, (int __user *) optval)) | 455 | if (get_user(val, (int __user *) optval)) |
427 | return -EFAULT; | 456 | return -EFAULT; |
@@ -437,7 +466,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
437 | /* If optlen==0, it is equivalent to val == 0 */ | 466 | /* If optlen==0, it is equivalent to val == 0 */ |
438 | 467 | ||
439 | if (ip_mroute_opt(optname)) | 468 | if (ip_mroute_opt(optname)) |
440 | return ip_mroute_setsockopt(sk,optname,optval,optlen); | 469 | return ip_mroute_setsockopt(sk, optname, optval, optlen); |
441 | 470 | ||
442 | err = 0; | 471 | err = 0; |
443 | lock_sock(sk); | 472 | lock_sock(sk); |
@@ -509,6 +538,12 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
509 | else | 538 | else |
510 | inet->cmsg_flags &= ~IP_CMSG_PASSSEC; | 539 | inet->cmsg_flags &= ~IP_CMSG_PASSSEC; |
511 | break; | 540 | break; |
541 | case IP_RECVORIGDSTADDR: | ||
542 | if (val) | ||
543 | inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR; | ||
544 | else | ||
545 | inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR; | ||
546 | break; | ||
512 | case IP_TOS: /* This sets both TOS and Precedence */ | 547 | case IP_TOS: /* This sets both TOS and Precedence */ |
513 | if (sk->sk_type == SOCK_STREAM) { | 548 | if (sk->sk_type == SOCK_STREAM) { |
514 | val &= ~3; | 549 | val &= ~3; |
@@ -549,7 +584,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
549 | goto e_inval; | 584 | goto e_inval; |
550 | if (optlen<1) | 585 | if (optlen<1) |
551 | goto e_inval; | 586 | goto e_inval; |
552 | if (val==-1) | 587 | if (val == -1) |
553 | val = 1; | 588 | val = 1; |
554 | if (val < 0 || val > 255) | 589 | if (val < 0 || val > 255) |
555 | goto e_inval; | 590 | goto e_inval; |
@@ -573,12 +608,12 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
573 | 608 | ||
574 | err = -EFAULT; | 609 | err = -EFAULT; |
575 | if (optlen >= sizeof(struct ip_mreqn)) { | 610 | if (optlen >= sizeof(struct ip_mreqn)) { |
576 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | 611 | if (copy_from_user(&mreq, optval, sizeof(mreq))) |
577 | break; | 612 | break; |
578 | } else { | 613 | } else { |
579 | memset(&mreq, 0, sizeof(mreq)); | 614 | memset(&mreq, 0, sizeof(mreq)); |
580 | if (optlen >= sizeof(struct in_addr) && | 615 | if (optlen >= sizeof(struct in_addr) && |
581 | copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr))) | 616 | copy_from_user(&mreq.imr_address, optval, sizeof(struct in_addr))) |
582 | break; | 617 | break; |
583 | } | 618 | } |
584 | 619 | ||
@@ -626,11 +661,11 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
626 | goto e_inval; | 661 | goto e_inval; |
627 | err = -EFAULT; | 662 | err = -EFAULT; |
628 | if (optlen >= sizeof(struct ip_mreqn)) { | 663 | if (optlen >= sizeof(struct ip_mreqn)) { |
629 | if (copy_from_user(&mreq,optval,sizeof(mreq))) | 664 | if (copy_from_user(&mreq, optval, sizeof(mreq))) |
630 | break; | 665 | break; |
631 | } else { | 666 | } else { |
632 | memset(&mreq, 0, sizeof(mreq)); | 667 | memset(&mreq, 0, sizeof(mreq)); |
633 | if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) | 668 | if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq))) |
634 | break; | 669 | break; |
635 | } | 670 | } |
636 | 671 | ||
@@ -808,7 +843,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
808 | err = -ENOBUFS; | 843 | err = -ENOBUFS; |
809 | break; | 844 | break; |
810 | } | 845 | } |
811 | gsf = kmalloc(optlen,GFP_KERNEL); | 846 | gsf = kmalloc(optlen, GFP_KERNEL); |
812 | if (!gsf) { | 847 | if (!gsf) { |
813 | err = -ENOBUFS; | 848 | err = -ENOBUFS; |
814 | break; | 849 | break; |
@@ -828,7 +863,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, | |||
828 | goto mc_msf_out; | 863 | goto mc_msf_out; |
829 | } | 864 | } |
830 | msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); | 865 | msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); |
831 | msf = kmalloc(msize,GFP_KERNEL); | 866 | msf = kmalloc(msize, GFP_KERNEL); |
832 | if (!msf) { | 867 | if (!msf) { |
833 | err = -ENOBUFS; | 868 | err = -ENOBUFS; |
834 | goto mc_msf_out; | 869 | goto mc_msf_out; |
@@ -971,9 +1006,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, | |||
971 | return -EOPNOTSUPP; | 1006 | return -EOPNOTSUPP; |
972 | 1007 | ||
973 | if (ip_mroute_opt(optname)) | 1008 | if (ip_mroute_opt(optname)) |
974 | return ip_mroute_getsockopt(sk,optname,optval,optlen); | 1009 | return ip_mroute_getsockopt(sk, optname, optval, optlen); |
975 | 1010 | ||
976 | if (get_user(len,optlen)) | 1011 | if (get_user(len, optlen)) |
977 | return -EFAULT; | 1012 | return -EFAULT; |
978 | if (len < 0) | 1013 | if (len < 0) |
979 | return -EINVAL; | 1014 | return -EINVAL; |
@@ -984,7 +1019,7 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, | |||
984 | case IP_OPTIONS: | 1019 | case IP_OPTIONS: |
985 | { | 1020 | { |
986 | unsigned char optbuf[sizeof(struct ip_options)+40]; | 1021 | unsigned char optbuf[sizeof(struct ip_options)+40]; |
987 | struct ip_options * opt = (struct ip_options*)optbuf; | 1022 | struct ip_options * opt = (struct ip_options *)optbuf; |
988 | opt->optlen = 0; | 1023 | opt->optlen = 0; |
989 | if (inet->opt) | 1024 | if (inet->opt) |
990 | memcpy(optbuf, inet->opt, | 1025 | memcpy(optbuf, inet->opt, |
@@ -1022,6 +1057,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, | |||
1022 | case IP_PASSSEC: | 1057 | case IP_PASSSEC: |
1023 | val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; | 1058 | val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; |
1024 | break; | 1059 | break; |
1060 | case IP_RECVORIGDSTADDR: | ||
1061 | val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; | ||
1062 | break; | ||
1025 | case IP_TOS: | 1063 | case IP_TOS: |
1026 | val = inet->tos; | 1064 | val = inet->tos; |
1027 | break; | 1065 | break; |
@@ -1154,13 +1192,13 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, | |||
1154 | len = 1; | 1192 | len = 1; |
1155 | if (put_user(len, optlen)) | 1193 | if (put_user(len, optlen)) |
1156 | return -EFAULT; | 1194 | return -EFAULT; |
1157 | if (copy_to_user(optval,&ucval,1)) | 1195 | if (copy_to_user(optval, &ucval, 1)) |
1158 | return -EFAULT; | 1196 | return -EFAULT; |
1159 | } else { | 1197 | } else { |
1160 | len = min_t(unsigned int, sizeof(int), len); | 1198 | len = min_t(unsigned int, sizeof(int), len); |
1161 | if (put_user(len, optlen)) | 1199 | if (put_user(len, optlen)) |
1162 | return -EFAULT; | 1200 | return -EFAULT; |
1163 | if (copy_to_user(optval,&val,len)) | 1201 | if (copy_to_user(optval, &val, len)) |
1164 | return -EFAULT; | 1202 | return -EFAULT; |
1165 | } | 1203 | } |
1166 | return 0; | 1204 | return 0; |
@@ -1178,7 +1216,7 @@ int ip_getsockopt(struct sock *sk, int level, | |||
1178 | !ip_mroute_opt(optname)) { | 1216 | !ip_mroute_opt(optname)) { |
1179 | int len; | 1217 | int len; |
1180 | 1218 | ||
1181 | if (get_user(len,optlen)) | 1219 | if (get_user(len, optlen)) |
1182 | return -EFAULT; | 1220 | return -EFAULT; |
1183 | 1221 | ||
1184 | lock_sock(sk); | 1222 | lock_sock(sk); |