* Re: [PATCH] DCCP: Typo
From: David S. Miller @ 2005-12-22 3:02 UTC (permalink / raw)
To: dccp
In-Reply-To: <cbec11ac0512201507p168d332bl8e23a2ade30935d0@mail.gmail.com>
From: Ian McDonald <imcdnzl@gmail.com>
Date: Wed, 21 Dec 2005 12:07:30 +1300
> Please apply to net-2.6 for 2.6.15 tree.
>
> I hope to actually change this behaviour shortly but this will help
> anybody grepping code at present.
>
> Signed-off-by: Ian McDonald <imcdnzl@gmail.com>
Applied, thanks Ian.
^ permalink raw reply
* [PATCH] DCCP: Typo
From: Ian McDonald @ 2005-12-20 23:07 UTC (permalink / raw)
To: dccp
Please apply to net-2.6 for 2.6.15 tree.
I hope to actually change this behaviour shortly but this will help
anybody grepping code at present.
Signed-off-by: Ian McDonald <imcdnzl@gmail.com>
---
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index ca03521..656e13e 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -1251,7 +1251,7 @@ static int dccp_v4_destroy_sock(struct s
struct dccp_sock *dp = dccp_sk(sk);
/*
- * DCCP doesn't use sk_qrite_queue, just sk_send_head
+ * DCCP doesn't use sk_write_queue, just sk_send_head
* for retransmissions
*/
if (sk->sk_send_head != NULL) {
^ permalink raw reply related
* Re: Discussion of ECN API?
From: Bruce Barnett @ 2005-12-20 17:38 UTC (permalink / raw)
To: dccp
In-Reply-To: <200512192127.jBJLRou27403@observ.crd.ge.COM>
> I'm not aware of any, at the same time I think DCCP should at least
> expose the info on ack vectors,so the question is: is the information
> available on ack vectors enough for you (the app writers?)?
Raw information may be harder to deal with, but at least all of the
information is there. After we have a better idea on the algorithms
needed for intelligent applications, we can provide a friendlier
interface. I don't think we know the best algorithms yet.
I think DCCP-enabled applications that are aware of congestion are key
to the next generation of streaming networking. TCP-based applications
don't care if the network is congested. Either the data arrives or it
doesn't. but VoIP/DCCP applications need to deal with latency and
reliability, and if quality drops, they must take responsibility.
Consider a VoIP application with a 10% packet loss. With some
vocoders, a 10% loss is unintelligable. So the application is sending
out 90% of the packets with zero effectiveness. Those packets are
useless, yet they are contributing to the congestion. I view this as
abuse to the network in two ways (1) sending out packets that are
contributing to congestion, and (2) doing so in such a way that the
packets are useless once they arrive. TCP doesn't abuse the
network like this.
Likewise, if the reliability was high, but the latency increased to 2
seconds per packet, then the usefullness of the bandwidth also
decreases as the mouth-to-ear delay increases.
Application adaption is key to minimizing network abuse for
streaming applications like VoIP.
Now we just have to figure out how to do it the right way. :-)
^ permalink raw reply
* Re: Discussion of ECN API?
From: Arnaldo Carvalho de Melo @ 2005-12-20 14:11 UTC (permalink / raw)
To: dccp
In-Reply-To: <200512192127.jBJLRou27403@observ.crd.ge.COM>
I'm not aware of any, at the same time I think DCCP should at least
expose the info on ack vectors,so the question is: is the information
available on ack vectors enough for you (the app writers?)?
- Arnaldo
On 12/19/05, Bruce Barnett <dccp@grymoire.com> wrote:
>
> I've been involved with the application perspective of DCCP
> programming, or rather - application programming aware of congestion,
> and I was wondering if there exists any API for other transport
> protocols that make congestion information available to the
> application? In particular, is there any existing API to extract the
> ECN information that can be used for a model? Or if it is missing, -
> how should it be specified?
>
> One approach is to provide an array of ECN values. But how big would
> this be? And how would it be packed?
>
> Or one could return a monotonicly increasing count of ECN failures. But
> what is a failure, as there are several kinds. Some errors should be
> handled by the kernel (Routers that don't support ECN, etc.). Others
> should be of interest to the application (there is congestion in the
> return path).
>
>
> One could also provide a error percentage, but how would this be calculated?
>
>
> Anyone have any ideas or resources I could check?
>
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe dccp" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Discussion of ECN API?
From: Bruce Barnett @ 2005-12-19 21:27 UTC (permalink / raw)
To: dccp
I've been involved with the application perspective of DCCP
programming, or rather - application programming aware of congestion,
and I was wondering if there exists any API for other transport
protocols that make congestion information available to the
application? In particular, is there any existing API to extract the
ECN information that can be used for a model? Or if it is missing, -
how should it be specified?
One approach is to provide an array of ECN values. But how big would
this be? And how would it be packed?
Or one could return a monotonicly increasing count of ECN failures. But
what is a failure, as there are several kinds. Some errors should be
handled by the kernel (Routers that don't support ECN, etc.). Others
should be of interest to the application (there is congestion in the
return path).
One could also provide a error percentage, but how would this be calculated?
Anyone have any ideas or resources I could check?
^ permalink raw reply
* second attempt for CCID2 patches
From: Andrea Bittau @ 2005-12-16 21:51 UTC (permalink / raw)
To: dccp
Here are the new CCID2 patches:
Do not process a packet twice when it's received in a state != DCCP_OPEN.
http://darkircop.org/1
Introduce ACK vector records. [using struct list_head]
http://darkircop.org/2
Introduce CCID2.
http://darkircop.org/3
^ permalink raw reply
* [RFC] Move DCCP to buffers for transmitting
From: Ian McDonald @ 2005-12-16 4:01 UTC (permalink / raw)
To: dccp
Folks,
I'm trying to change DCCP transmission to use buffers like TCP rather
than current mechanism.
This code is not yet quite ready as have kernel asserts as per below:
Dec 16 16:50:59 localhost kernel: KERNEL: assertion
(skb_queue_empty(&sk->sk_write_queue)) failed at net/core/stream.c
(277)
Dec 16 16:50:59 localhost kernel: KERNEL: assertion
(!sk->sk_wmem_queued) failed at net/core/stream.c (282)
Dec 16 16:50:59 localhost kernel: KERNEL: assertion
(!sk->sk_forward_alloc) failed at net/core/stream.c (283)
Dec 16 16:50:59 localhost kernel: KERNEL: assertion
(!sk->sk_wmem_queued) failed at net/ipv4/af_inet.c (147)
Dec 16 16:50:59 localhost kernel: KERNEL: assertion
(!sk->sk_forward_alloc) failed at net/ipv4/af_inet.c (148)
I thought I would get it out there for discussion as I am finishing
for weekend now probably and wouldn't mind feedback on general
principle of my code. This is against davem's net-2.6.16 as of a day
or so ago. Code still needs tidying up as well in style a little....
Ian
--
Ian McDonald
http://wand.net.nz/~iam4
WAND Network Research Group
University of Waikato
New Zealand
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
diff --git a/include/net/sock.h b/include/net/sock.h
index 91d2895..49bd617 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1053,6 +1053,23 @@ static inline void sk_charge_skb(struct
sk->sk_forward_alloc -= skb->truesize;
}
+static inline void skb_entail(struct sock *sk, struct sk_buff *skb)
+{
+ skb->csum = 0;
+ skb_header_release(skb);
+ __skb_queue_tail(&sk->sk_write_queue, skb);
+ sk_charge_skb(sk, skb);
+ if (!sk->sk_send_head)
+ sk->sk_send_head = skb;
+}
+
+static inline void skb_update_send_head(struct sock *sk, struct sk_buff *skb)
+{
+ sk->sk_send_head = skb->next;
+ if (sk->sk_send_head = (struct sk_buff *)&sk->sk_write_queue)
+ sk->sk_send_head = NULL;
+}
+
static inline int skb_copy_to_page(struct sock *sk, char __user *from,
struct sk_buff *skb, struct page *page,
int off, int copy)
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index 93f26dd..3ecc417 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -126,7 +126,7 @@ extern void dccp_send_delayed_ack(struct
extern void dccp_send_sync(struct sock *sk, const u64 seq,
const enum dccp_pkt_type pkt_type);
-extern int dccp_write_xmit(struct sock *sk, struct sk_buff *skb, long *timeo);
+extern void dccp_write_xmit(struct sock *sk);
extern void dccp_write_space(struct sock *sk);
extern void dccp_init_xmit_timers(struct sock *sk);
diff --git a/net/dccp/output.c b/net/dccp/output.c
index 95a3c2c..1618106 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -26,12 +26,14 @@ static inline void dccp_event_ack_sent(s
inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
}
+#if 0
static inline void dccp_skb_entail(struct sock *sk, struct sk_buff *skb)
{
skb_set_owner_w(skb, sk);
WARN_ON(sk->sk_send_head);
sk->sk_send_head = skb;
}
+#endif
/*
* All SKB's seen here are completely headerless. It is our
@@ -226,37 +228,47 @@ do_interrupted:
goto out;
}
-int dccp_write_xmit(struct sock *sk, struct sk_buff *skb, long *timeo)
+void dccp_write_xmit(struct sock *sk)
{
const struct dccp_sock *dp = dccp_sk(sk);
- int err = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb,
+ struct sk_buff *skb;
+ long timeo = 2000; /* fixme iancrap - 2 second default */
+
+ while ((skb = sk->sk_send_head)) {
+ int err = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb,
skb->len);
- if (err > 0)
- err = dccp_wait_for_ccid(sk, skb, timeo);
+ if (err > 0)
+ err = dccp_wait_for_ccid(sk, skb, &timeo);
- if (err = 0) {
- struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
- const int len = skb->len;
-
- if (sk->sk_state = DCCP_PARTOPEN) {
- /* See 8.1.5. Handshake Completion */
- inet_csk_schedule_ack(sk);
- inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
+ if (err = 0) {
+ struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
+ const int len = skb->len;
+
+ if (sk->sk_state = DCCP_PARTOPEN) {
+ /* See 8.1.5. Handshake Completion */
+ inet_csk_schedule_ack(sk);
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
inet_csk(sk)->icsk_rto,
DCCP_RTO_MAX);
- dcb->dccpd_type = DCCP_PKT_DATAACK;
- } else if (dccp_ack_pending(sk))
- dcb->dccpd_type = DCCP_PKT_DATAACK;
- else
- dcb->dccpd_type = DCCP_PKT_DATA;
-
- err = dccp_transmit_skb(sk, skb);
- ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len);
- } else
- kfree_skb(skb);
+ dcb->dccpd_type = DCCP_PKT_DATAACK;
+ } else if (dccp_ack_pending(sk))
+ dcb->dccpd_type = DCCP_PKT_DATAACK;
+ else
+ dcb->dccpd_type = DCCP_PKT_DATA;
+ err = dccp_transmit_skb(sk, skb);
+ ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len);
+ }
+#if 0
+ } else
+ kfree_skb(skb);
+#endif
+ skb_update_send_head(sk, skb);
+ }
+#if 0
return err;
+#endif
}
int dccp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
@@ -414,7 +426,8 @@ int dccp_connect(struct sock *sk)
DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST;
skb->csum = 0;
- dccp_skb_entail(sk, skb);
+ skb_entail(sk, skb);
+ /* iancrap */
dccp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL));
DCCP_INC_STATS(DCCP_MIB_ACTIVEOPENS);
@@ -526,7 +539,8 @@ void dccp_send_close(struct sock *sk, co
DCCP_PKT_CLOSE : DCCP_PKT_CLOSEREQ;
if (active) {
- dccp_skb_entail(sk, skb);
+ skb_entail(sk, skb);
+ /* iancrap */
dccp_transmit_skb(sk, skb_clone(skb, prio));
} else
dccp_transmit_skb(sk, skb);
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 40a4c68..c6a1138 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -387,6 +387,8 @@ int dccp_sendmsg(struct kiocb *iocb, str
lock_sock(sk);
timeo = sock_sndtimeo(sk, noblock);
+ /* iancrap - use timeo for memory allocation */
+
/*
* We have to use sk_stream_wait_connect here to set sk_write_pending,
* so that the trick in dccp_rcv_request_sent_state_process.
@@ -408,17 +410,9 @@ int dccp_sendmsg(struct kiocb *iocb, str
if (rc != 0)
goto out_discard;
- rc = dccp_write_xmit(sk, skb, &timeo);
- /*
- * XXX we don't use sk_write_queue, so just discard the packet.
- * Current plan however is to _use_ sk_write_queue with
- * an algorith similar to tcp_sendmsg, where the main difference
- * is that in DCCP we have to respect packet boundaries, so
- * no coalescing of skbs.
- *
- * This bug was _quickly_ found & fixed by just looking at an OSTRA
- * generated callgraph 8) -acme
- */
+ skb_entail(sk, skb);
+
+ dccp_write_xmit(sk);
out_release:
release_sock(sk);
return rc ? : len;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 00aa80e..e0b53b4 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -458,19 +458,14 @@ static inline int forced_push(struct tcp
return after(tp->write_seq, tp->pushed_seq + (tp->max_window >> 1));
}
-static inline void skb_entail(struct sock *sk, struct tcp_sock *tp,
+static inline void tcp_skb_entail(struct sock *sk, struct tcp_sock *tp,
struct sk_buff *skb)
{
- skb->csum = 0;
+ skb_entail(sk,skb);
TCP_SKB_CB(skb)->seq = tp->write_seq;
TCP_SKB_CB(skb)->end_seq = tp->write_seq;
TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK;
TCP_SKB_CB(skb)->sacked = 0;
- skb_header_release(skb);
- __skb_queue_tail(&sk->sk_write_queue, skb);
- sk_charge_skb(sk, skb);
- if (!sk->sk_send_head)
- sk->sk_send_head = skb;
if (tp->nonagle & TCP_NAGLE_PUSH)
tp->nonagle &= ~TCP_NAGLE_PUSH;
}
@@ -539,7 +534,7 @@ new_segment:
if (!skb)
goto wait_for_memory;
- skb_entail(sk, tp, skb);
+ tcp_skb_entail(sk, tp, skb);
copy = size_goal;
}
@@ -730,7 +725,7 @@ new_segment:
NETIF_F_HW_CSUM))
skb->ip_summed = CHECKSUM_HW;
- skb_entail(sk, tp, skb);
+ tcp_skb_entail(sk, tp, skb);
copy = size_goal;
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 3a0a914..1ec9128 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -51,12 +51,10 @@ int sysctl_tcp_retrans_collapse = 1;
*/
int sysctl_tcp_tso_win_divisor = 3;
-static inline void update_send_head(struct sock *sk, struct tcp_sock *tp,
+static inline void tcp_update_send_head(struct sock *sk, struct tcp_sock *tp,
struct sk_buff *skb)
{
- sk->sk_send_head = skb->next;
- if (sk->sk_send_head = (struct sk_buff *)&sk->sk_write_queue)
- sk->sk_send_head = NULL;
+ skb_update_send_head(sk, skb);
tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
tcp_packets_out_inc(sk, tp, skb);
}
@@ -1060,7 +1058,7 @@ static int tcp_write_xmit(struct sock *s
/* Advance the send_head. This one is sent out.
* This call will increment packets_out.
*/
- update_send_head(sk, tp, skb);
+ tcp_update_send_head(sk, tp, skb);
tcp_minshall_update(tp, mss_now, skb);
sent_pkts++;
@@ -1128,7 +1126,7 @@ void tcp_push_one(struct sock *sk, unsig
TCP_SKB_CB(skb)->when = tcp_time_stamp;
if (likely(!tcp_transmit_skb(sk, skb, 1, sk->sk_allocation))) {
- update_send_head(sk, tp, skb);
+ tcp_update_send_head(sk, tp, skb);
tcp_cwnd_validate(sk, tp);
return;
}
@@ -2048,7 +2046,7 @@ int tcp_write_wakeup(struct sock *sk)
TCP_SKB_CB(skb)->when = tcp_time_stamp;
err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
if (!err) {
- update_send_head(sk, tp, skb);
+ tcp_update_send_head(sk, tp, skb);
}
return err;
} else {
^ permalink raw reply related
* Re: [PATCH 2/3] DCCP: Introduce CCID2
From: Arnaldo Carvalho de Melo @ 2005-12-13 13:11 UTC (permalink / raw)
To: dccp
In-Reply-To: <20051213093224.GB8532@tribal.sorbonet.org>
On 12/13/05, Andrea Bittau <a.bittau@cs.ucl.ac.uk> wrote:
> Initial support for CCID2 [TCP-like]. CCID2 is missing:
> * ECN nonces.
> * Detecting ECN marked ACKs.
> * Determining when flows go silent [ack of acks business].
>
> Support for changing the receivier and transmitter half-connection CCID is
> available through setsockopt.
Its awesome that you followed the style and managed to fit CCID2 into
the existing pluggable ccid infrastructure, I was fearing we'd need to
redesign it to support CCID2 properly, it may well be that we decide
to do that to converge with TCP to a common congestion control
infrastructure, but thanks a lot for trying it with the current
framework! I'll be looking at it and hope Ian and Dave can provide a
helping hand with much appreciated comments on your work!
- Arnaldo
^ permalink raw reply
* Re: [PATCH 3/3] DCCP: fix connect() race condition upon short connections
From: Arnaldo Carvalho de Melo @ 2005-12-13 13:07 UTC (permalink / raw)
To: dccp
In-Reply-To: <20051213093402.GC8532@tribal.sorbonet.org>
On 12/13/05, Andrea Bittau <a.bittau@cs.ucl.ac.uk> wrote:
> The connect system call will initiate a connection, poll whether the connection
> has occured and right before returning, it will check whether the connection was
> shut-down. In such cases, it will return -1. If a connection is really short,
> it is possible that the connection occurs and terminates before connect()
> returns. Upon checking that the connection is closed, connect() will
> incorrectly return -1 when the transfer did occur.
>
> I was able to reproduce this race condition consistently by having a server send
> 3 packets upon connection and disconnect right after. I believe this issue
> might exist in TCP too although mitigated by the higher number of CLOSE states.
>
> I believe there is a better solution than the one I proposed [simplest I could
> think of].
Yes, I believe we should take a look at one that doesn't involves
tasklets, will try to come up with something today, but anyway, thanks
for pointing out the issue (again)!
- Arnaldo
^ permalink raw reply
* Re: [PATCH 1/3] DCCP: Introduce ACK vector records
From: Arnaldo Carvalho de Melo @ 2005-12-13 13:05 UTC (permalink / raw)
To: dccp
In-Reply-To: <20051213093130.GA8532@tribal.sorbonet.org>
On 12/13/05, Andrea Bittau <a.bittau@cs.ucl.ac.uk> wrote:
> Initial support for ACK vector records. Currently there is a fixed
> number of records which doesn't grow at run-time. Need to decide
> what to do [currently dies] when we run out of records [allocate more,
> return -1?].
>
> Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
>
> ---
>
> diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig
> index 187ac18..f39abaf 100644
> --- a/net/dccp/Kconfig
> +++ b/net/dccp/Kconfig
> @@ -36,6 +36,13 @@ config IP_DCCP_DEBUG
>
> Just say N.
>
> +config IP_DCCP_ACKVEC
> + bool "DCCP ACK vector"
> + ---help---
> + Enable the use of ACK vectors.
> +
> + Just say N.
> +
> config IP_DCCP_UNLOAD_HACK
> depends on IP_DCCP=m && IP_DCCP_CCID3=m
> bool "DCCP control sock unload hack"
> diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
> index c9a62cc..12a1fa5 100644
> --- a/net/dccp/ackvec.c
> +++ b/net/dccp/ackvec.c
> @@ -17,15 +17,84 @@
>
> #include <net/sock.h>
>
> +static void dccp_ackvec_insert_avr(struct dccp_ackvec *av,
> + struct dccp_ackvec_record *avr)
> +{
> + struct dccp_ackvec_record *pos, *prev;
> +
> + BUG_ON(av = NULL);
> + BUG_ON(avr = NULL);
> +
> +#if CONFIG_IP_DCCP_DEBUG
> + dccp_pr_debug("SEQ=%llu %p\n", avr->dccpavr_ack_seqno, avr);
> + pos = av->dccpav_record;
> +
> + while (pos) {
> + dccp_pr_debug("Printing seq=%llu %p\n", pos->dccpavr_ack_seqno,
> + pos);
> + pos = pos->dccpavr_next;
> + }
> +#endif
> + pos = av->dccpav_record;
> +
> + /* first record */
> + if (!pos) {
> + avr->dccpavr_next = avr->dccpavr_prev = NULL;
> + av->dccpav_record = avr;
> + return;
> + }
> +
> + /* find where to insert record... ordered by seqno */
> + while (pos) {
> + prev = pos;
> + if (avr->dccpavr_ack_seqno > pos->dccpavr_ack_seqno)
> + break;
> + pos = pos->dccpavr_next;
> + }
> +
> + /* last record */
> + if (!pos) {
> + BUG_ON(prev = NULL);
> +
> + prev->dccpavr_next = avr;
> + avr->dccpavr_prev = prev;
> + avr->dccpavr_next = NULL;
> + return;
> + }
> +
> + /* in the middle somewhere or head */
> + avr->dccpavr_next = pos;
> + avr->dccpavr_prev = pos->dccpavr_prev;
> + pos->dccpavr_prev = avr;
> +
> + /* middle */
> + if (avr->dccpavr_prev) {
> + BUG_ON(avr->dccpavr_prev = NULL);
> +
> + avr->dccpavr_prev->dccpavr_next = avr;
> + }
> + /* head */
> + else {
> + av->dccpav_record = avr;
> + }
> +}
> +
> int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
> {
> struct dccp_sock *dp = dccp_sk(sk);
> struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec;
> - int len = av->dccpav_vec_len + 2;
> + int len;
> struct timeval now;
> u32 elapsed_time;
> unsigned char *to, *from;
> + struct dccp_ackvec_record *avr;
> +#ifdef CONFIG_IP_DCCP_DEBUG
> + const char *debug_prefix = dp->dccps_role = DCCP_ROLE_CLIENT ?
> + "CLIENT rx ack: " : "server rx ack: ";
> +#endif
>
> + BUG_ON(av = NULL);
> + len = av->dccpav_vec_len + 2;
> dccp_timestamp(sk, &now);
> elapsed_time = timeval_delta(&now, &av->dccpav_time) / 10;
>
> @@ -35,19 +104,24 @@ int dccp_insert_option_ackvec(struct soc
> if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
> return -1;
>
> - /*
> - * XXX: now we have just one ack vector sent record, so
> - * we have to wait for it to be cleared.
> - *
> - * Of course this is not acceptable, but this is just for
> - * basic testing now.
> + /* We don't have space to store ACK vector.
> + * Two options:
> + * 1) Allocate more space.
> + * 2) Error.
> + * We do 2 because it's easy =D
> + * -sorbo.
> */
> - if (av->dccpav_ack_seqno != DCCP_MAX_SEQNO + 1)
> + if (av->dccpav_record_pool_free = av->dccpav_record_pool_len) {
> + BUG_ON(1);
> return -1;
> + }
> + avr = av->dccpav_record_pool[av->dccpav_record_pool_free++];
> + BUG_ON(avr = NULL);
>
> DCCP_SKB_CB(skb)->dccpd_opt_len += len;
>
> to = skb_push(skb, len);
> + BUG_ON(to = NULL);
> *to++ = DCCPO_ACK_VECTOR_0;
> *to++ = len;
>
> @@ -55,8 +129,8 @@ int dccp_insert_option_ackvec(struct soc
> from = av->dccpav_buf + av->dccpav_buf_head;
>
> /* Check if buf_head wraps */
> - if (av->dccpav_buf_head + len > av->dccpav_vec_len) {
> - const u32 tailsize = (av->dccpav_vec_len - av->dccpav_buf_head);
> + if (av->dccpav_buf_head + len > av->dccpav_buf_len) {
> + const u32 tailsize = (av->dccpav_buf_len - av->dccpav_buf_head);
>
> memcpy(to, from, tailsize);
> to += tailsize;
> @@ -76,26 +150,37 @@ int dccp_insert_option_ackvec(struct soc
> *
> * This implemention uses just one ack record for now.
> */
> - av->dccpav_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq;
> - av->dccpav_ack_ptr = av->dccpav_buf_head;
> - av->dccpav_ack_ackno = av->dccpav_buf_ackno;
> - av->dccpav_ack_nonce = av->dccpav_buf_nonce;
> - av->dccpav_sent_len = av->dccpav_vec_len;
> + avr->dccpavr_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq;
> + avr->dccpavr_ack_ptr = av->dccpav_buf_head;
> + avr->dccpavr_ack_ackno = av->dccpav_buf_ackno;
> + avr->dccpavr_ack_nonce = av->dccpav_buf_nonce;
> + avr->dccpavr_sent_len = av->dccpav_vec_len;
>
> + dccp_ackvec_insert_avr(av, avr);
> dccp_pr_debug("%sACK Vector 0, len=%d, ack_seqno=%llu, "
> "ack_ackno=%llu\n",
> - debug_prefix, av->dccpav_sent_len,
> - (unsigned long long)av->dccpav_ack_seqno,
> - (unsigned long long)av->dccpav_ack_ackno);
> - return -1;
> + debug_prefix, avr->dccpavr_sent_len,
> + (unsigned long long)avr->dccpavr_ack_seqno,
> + (unsigned long long)avr->dccpavr_ack_ackno);
> +
> + return 0;
> }
>
> struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len,
> const gfp_t priority)
> {
> + struct dccp_ackvec_record *avr;
> struct dccp_ackvec *av = kmalloc(sizeof(*av) + len, priority);
>
> if (av != NULL) {
> + /* Initial pool of records. We should allow for this pool to
> + * grow at run-time or just put a maximum value for it...
> + * For now, it's a maximum value.
> + * -sorbo.
> + */
> + unsigned int records = 128;
> + unsigned int i;
> +
> av->dccpav_buf_len = len;
> av->dccpav_buf_head > av->dccpav_buf_tail = av->dccpav_buf_len - 1;
> @@ -106,13 +191,40 @@ struct dccp_ackvec *dccp_ackvec_alloc(co
> av->dccpav_time.tv_sec = 0;
> av->dccpav_time.tv_usec = 0;
> av->dccpav_sent_len = av->dccpav_vec_len = 0;
> - }
> + av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
>
> + av->dccpav_record_pool = kmalloc(
> + sizeof(*av->dccpav_record_pool) * records, priority);
> + if (av->dccpav_record_pool = NULL)
> + goto fail_rec_pool;
> +
> + avr = kmalloc(sizeof(*avr) * records, priority);
> + if (avr = NULL)
> + goto fail_avr;
> +
> + av->dccpav_record = NULL;
> + av->dccpav_record_pool_len = records;
> + /* position 0 contains original pointer, so we can free it */
> + av->dccpav_record_pool_free = 1;
> + for (i = 0; i < av->dccpav_record_pool_len; i++)
> + av->dccpav_record_pool[i] = avr++;
> + }
> +out:
> return av;
> +
> +fail_avr:
> + kfree(av->dccpav_record_pool);
> +
> +fail_rec_pool:
> + kfree(av);
> + av = NULL;
> + goto out;
> }
>
> void dccp_ackvec_free(struct dccp_ackvec *av)
> {
> + kfree(av->dccpav_record_pool[0]);
> + kfree(av->dccpav_record_pool);
> kfree(av);
> }
>
> @@ -291,43 +403,61 @@ void dccp_ackvec_print(const struct dccp
> }
> #endif
>
> -static void dccp_ackvec_trow_away_ack_record(struct dccp_ackvec *av)
> +static void dccp_ackvec_throw_away_ack_record(struct dccp_ackvec *av,
> + struct dccp_ackvec_record *avr)
> {
> - /*
> - * As we're keeping track of the ack vector size (dccpav_vec_len) and
> - * the sent ack vector size (dccpav_sent_len) we don't need
> - * dccpav_buf_tail at all, but keep this code here as in the future
> - * we'll implement a vector of ack records, as suggested in
> - * draft-ietf-dccp-spec-11.txt Appendix A. -acme
> + /* In appendix A of the spec, I think that ack_ptr + 1 means "ack_ptr
> + * one slot to the left". I.e. we already acked ACK_PTR so we only
> + * wish to ack new stuff in the future. Thus ack_ptr - 1 in this code.
> + * -sorbo.
> */
> -#if 0
> - av->dccpav_buf_tail = av->dccpav_ack_ptr + 1;
> - if (av->dccpav_buf_tail >= av->dccpav_vec_len)
> - av->dccpav_buf_tail -= av->dccpav_vec_len;
> -#endif
> - av->dccpav_vec_len -= av->dccpav_sent_len;
> + /* XXX: we don't use buf_tail but update it anyway... */
> + av->dccpav_buf_tail = avr->dccpavr_ack_ptr;
> + if (!av->dccpav_buf_tail)
> + av->dccpav_buf_tail = av->dccpav_buf_len;
> + av->dccpav_buf_tail--;
> +
> + av->dccpav_vec_len -= avr->dccpavr_sent_len;
> +
> + /* free records */
> + dccp_pr_debug("free avr: %llu %p\n", avr->dccpavr_ack_seqno, avr);
> +
> + if (avr->dccpavr_prev)
> + avr->dccpavr_prev->dccpavr_next = NULL;
> + else
> + av->dccpav_record = NULL;
> + while (avr) {
> + av->dccpav_record_pool_free--;
> + BUG_ON(av->dccpav_record_pool_free = 0);
> +
> + av->dccpav_record_pool[av->dccpav_record_pool_free] = avr;
> + avr = avr->dccpavr_next;
> + }
> }
>
> void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk,
> const u64 ackno)
> {
> - /* Check if we actually sent an ACK vector */
> - if (av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1)
> - return;
> -
> - if (ackno = av->dccpav_ack_seqno) {
> + struct dccp_ackvec_record *avr;
> +
> + avr = av->dccpav_record;
> + while (avr) {
> + if (ackno = avr->dccpavr_ack_seqno) {
> #ifdef CONFIG_IP_DCCP_DEBUG
> - struct dccp_sock *dp = dccp_sk(sk);
> - const char *debug_prefix = dp->dccps_role = DCCP_ROLE_CLIENT ?
> - "CLIENT rx ack: " : "server rx ack: ";
> + struct dccp_sock *dp = dccp_sk(sk);
> + const char *debug_prefix > + dp->dccps_role = DCCP_ROLE_CLIENT ?
> + "CLIENT rx ack: " : "server rx ack: ";
> #endif
> - dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, "
> - "ack_ackno=%llu, ACKED!\n",
> - debug_prefix, 1,
> - (unsigned long long)av->dccpav_ack_seqno,
> - (unsigned long long)av->dccpav_ack_ackno);
> - dccp_ackvec_trow_away_ack_record(av);
> - av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
> + dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, "
> + "ack_ackno=%llu, ACKED!\n",
> + debug_prefix, 1,
> + (unsigned long long)avr->dccpavr_ack_seqno,
> + (unsigned long long)avr->dccpavr_ack_ackno);
> + dccp_ackvec_throw_away_ack_record(av, avr);
> + break;
> + }
> + avr = avr->dccpavr_next;
> }
> }
>
> @@ -337,26 +467,12 @@ static void dccp_ackvec_check_rcv_ackvec
> const unsigned char *vector)
> {
> unsigned char i;
> + struct dccp_ackvec_record *avr;
>
> + avr = av->dccpav_record;
> /* Check if we actually sent an ACK vector */
> - if (av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1)
> + if (!avr)
> return;
> - /*
> - * We're in the receiver half connection, so if the received an ACK
> - * vector ackno (e.g. 50) before dccpav_ack_seqno (e.g. 52), we're
> - * not interested.
> - *
> - * Extra explanation with example:
> - *
> - * if we received an ACK vector with ackno 50, it can only be acking
> - * 50, 49, 48, etc, not 52 (the seqno for the ACK vector we sent).
> - */
> - /* dccp_pr_debug("is %llu < %llu? ", ackno, av->dccpav_ack_seqno); */
> - if (before48(ackno, av->dccpav_ack_seqno)) {
> - /* dccp_pr_debug_cat("yes\n"); */
> - return;
> - }
> - /* dccp_pr_debug_cat("no\n"); */
>
> i = len;
> while (i--) {
> @@ -365,11 +481,22 @@ static void dccp_ackvec_check_rcv_ackvec
>
> dccp_set_seqno(&ackno_end_rl, ackno - rl);
>
> + /* if our AVR sequence number is greater than the ack, go
> + * forward in the AVR list until it is not so.
> + */
> + while (avr) {
> + if (!after48(avr->dccpavr_ack_seqno, ackno))
> + break;
> + avr = avr->dccpavr_next;
> + }
> + if (!avr)
> + break;
> +
> /*
> * dccp_pr_debug("is %llu <= %llu <= %llu? ", ackno_end_rl,
> * av->dccpav_ack_seqno, ackno);
> */
> - if (between48(av->dccpav_ack_seqno, ackno_end_rl, ackno)) {
> + if (between48(avr->dccpavr_ack_seqno, ackno_end_rl, ackno)) {
> const u8 state = (*vector &
> DCCP_ACKVEC_STATE_MASK) >> 6;
> /* dccp_pr_debug_cat("yes\n"); */
> @@ -389,14 +516,11 @@ static void dccp_ackvec_check_rcv_ackvec
> av->dccpav_ack_seqno,
> (unsigned long long)
> av->dccpav_ack_ackno);
> - dccp_ackvec_trow_away_ack_record(av);
> + dccp_ackvec_throw_away_ack_record(av, avr);
> + break;
> }
> - /*
> - * If dccpav_ack_seqno was not received, no problem
> - * we'll send another ACK vector.
> - */
> - av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
> - break;
> + /* If it wasn't received, continue scanning... we might
> + * find another one. */
> }
> /* dccp_pr_debug_cat("no\n"); */
>
> diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h
> index d0fd6c6..4aa16df 100644
> --- a/net/dccp/ackvec.h
> +++ b/net/dccp/ackvec.h
> @@ -26,6 +26,19 @@
> #define DCCP_ACKVEC_STATE_MASK 0xC0 /* 11000000 */
> #define DCCP_ACKVEC_LEN_MASK 0x3F /* 00111111 */
>
> +/* ACK vector record as defined in Appendix A of spec.
> + * The list is sorted by ack_seqno
> + */
> +struct dccp_ackvec_record {
> + u64 dccpavr_ack_seqno;
> + unsigned int dccpavr_ack_ptr;
> + u64 dccpavr_ack_ackno;
> + u8 dccpavr_ack_nonce;
> + unsigned int dccpavr_sent_len;
> + struct dccp_ackvec_record* dccpavr_next;
> + struct dccp_ackvec_record* dccpavr_prev;
> +};
> +
Use struct list_head
> /** struct dccp_ackvec - ack vector
> *
> * This data structure is the one defined in the DCCP draft
> @@ -54,19 +67,23 @@
> * @dccpav_buf - circular buffer of acknowledgeable packets
> */
> struct dccp_ackvec {
> - unsigned int dccpav_buf_head;
> - unsigned int dccpav_buf_tail;
> - u64 dccpav_buf_ackno;
> - u64 dccpav_ack_seqno;
> - u64 dccpav_ack_ackno;
> - unsigned int dccpav_ack_ptr;
> - unsigned int dccpav_sent_len;
> - unsigned int dccpav_vec_len;
> - unsigned int dccpav_buf_len;
> - struct timeval dccpav_time;
> - u8 dccpav_buf_nonce;
> - u8 dccpav_ack_nonce;
> - u8 dccpav_buf[0];
> + unsigned int dccpav_buf_head;
> + unsigned int dccpav_buf_tail;
> + u64 dccpav_buf_ackno;
> + u64 dccpav_ack_seqno;
> + u64 dccpav_ack_ackno;
> + unsigned int dccpav_ack_ptr;
> + unsigned int dccpav_sent_len;
> + unsigned int dccpav_vec_len;
> + unsigned int dccpav_buf_len;
> + struct timeval dccpav_time;
> + u8 dccpav_buf_nonce;
> + u8 dccpav_ack_nonce;
> + struct dccp_ackvec_record* dccpav_record;
> + struct dccp_ackvec_record** dccpav_record_pool;
> + unsigned int dccpav_record_pool_free;
> + unsigned int dccpav_record_pool_len;
> + u8 dccpav_buf[0];
> };
Try to not reflow the struct for source code alignment at first, to
ease review, then send a patch just reflowing.
> struct sock;
> diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
> index 93f26dd..1dcd835 100644
> --- a/net/dccp/dccp.h
> +++ b/net/dccp/dccp.h
> @@ -226,7 +226,8 @@ extern int dccp_child_process(struct soc
> extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
> struct dccp_hdr *dh, unsigned len);
> extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
> - const struct dccp_hdr *dh, const unsigned len);
> + const struct dccp_hdr *dh, const unsigned len,
> + int fastlane);
>
> extern int dccp_v4_init_sock(struct sock *sk);
> extern int dccp_v4_destroy_sock(struct sock *sk);
> diff --git a/net/dccp/input.c b/net/dccp/input.c
> index 55e921b..757539a 100644
> --- a/net/dccp/input.c
> +++ b/net/dccp/input.c
> @@ -152,27 +152,36 @@ static int dccp_check_seqno(struct sock
> }
>
> int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
> - const struct dccp_hdr *dh, const unsigned len)
> + const struct dccp_hdr *dh, const unsigned len,
> + int fastlane)
> {
> struct dccp_sock *dp = dccp_sk(sk);
>
> - if (dccp_check_seqno(sk, skb))
> - goto discard;
> + /* If called from the DCCP_OPEN state [fast path] then perform checks
> + * as upper layers don't. If called from DCCP_PARTOPEN, checks will
> + * be done by upper layers... so don't repeat them. [else ack vector
> + * will be added twice! and packet will be dropped]
> + * -sorbo
> + */
> + if (fastlane) {
This is unrelated to ack vector records, so should be sent as a
separate changeset and discussed on its own merits
> + if (dccp_check_seqno(sk, skb))
> + goto discard;
>
> - if (dccp_parse_options(sk, skb))
> - goto discard;
> + if (dccp_parse_options(sk, skb))
> + goto discard;
>
> - if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
> - dccp_event_ack_recv(sk, skb);
> + if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
> + dccp_event_ack_recv(sk, skb);
>
> - if (dp->dccps_options.dccpo_send_ack_vector &&
> - dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
> - DCCP_SKB_CB(skb)->dccpd_seq,
> - DCCP_ACKVEC_STATE_RECEIVED))
> - goto discard;
> + if (dp->dccps_options.dccpo_send_ack_vector &&
> + dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
> + DCCP_SKB_CB(skb)->dccpd_seq,
> + DCCP_ACKVEC_STATE_RECEIVED))
> + goto discard;
>
> - ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
> - ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
> + ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
> + ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
> + }
>
> switch (dccp_hdr(skb)->dccph_type) {
> case DCCP_PKT_DATAACK:
> @@ -288,6 +297,12 @@ static int dccp_rcv_request_sent_state_p
> goto out_invalid_packet;
> }
>
> + if (dp->dccps_options.dccpo_send_ack_vector &&
> + dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
> + DCCP_SKB_CB(skb)->dccpd_seq,
> + DCCP_ACKVEC_STATE_RECEIVED))
> + goto out_invalid_packet; /* FIXME: change error code */
> +
> dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq;
> dccp_update_gsr(sk, dp->dccps_isr);
> /*
> @@ -400,7 +415,7 @@ static int dccp_rcv_respond_partopen_sta
>
> if (dh->dccph_type = DCCP_PKT_DATAACK ||
> dh->dccph_type = DCCP_PKT_DATA) {
> - dccp_rcv_established(sk, skb, dh, len);
> + dccp_rcv_established(sk, skb, dh, len, 0);
> queued = 1; /* packet was queued
> (by dccp_rcv_established) */
> }
> @@ -474,14 +489,14 @@ int dccp_rcv_state_process(struct sock *
> if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
> dccp_event_ack_recv(sk, skb);
>
> - ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
> - ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
> -
> if (dp->dccps_options.dccpo_send_ack_vector &&
> dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
> DCCP_SKB_CB(skb)->dccpd_seq,
> DCCP_ACKVEC_STATE_RECEIVED))
> goto discard;
> +
> + ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
> + ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
> }
>
> /*
> diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
> index f935659..5fdad99 100644
> --- a/net/dccp/ipv4.c
> +++ b/net/dccp/ipv4.c
> @@ -795,7 +795,7 @@ int dccp_v4_do_rcv(struct sock *sk, stru
> struct dccp_hdr *dh = dccp_hdr(skb);
>
> if (sk->sk_state = DCCP_OPEN) { /* Fast path */
> - if (dccp_rcv_established(sk, skb, dh, skb->len))
> + if (dccp_rcv_established(sk, skb, dh, skb->len, 1))
> goto reset;
> return 0;
> }
> diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
> index ba50c96..14f1c13 100644
> --- a/net/dccp/ipv6.c
> +++ b/net/dccp/ipv6.c
> @@ -1159,7 +1159,7 @@ static int dccp_v6_do_rcv(struct sock *s
> opt_skb = skb_clone(skb, GFP_ATOMIC);
>
> if (sk->sk_state = DCCP_OPEN) { /* Fast path */
> - if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len))
> + if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len, 1))
> goto reset;
> return 0;
> }
^ permalink raw reply
* [PATCH 3/3] DCCP: fix connect() race condition upon short connections
From: Andrea Bittau @ 2005-12-13 9:34 UTC (permalink / raw)
To: dccp
The connect system call will initiate a connection, poll whether the connection
has occured and right before returning, it will check whether the connection was
shut-down. In such cases, it will return -1. If a connection is really short,
it is possible that the connection occurs and terminates before connect()
returns. Upon checking that the connection is closed, connect() will
incorrectly return -1 when the transfer did occur.
I was able to reproduce this race condition consistently by having a server send
3 packets upon connection and disconnect right after. I believe this issue
might exist in TCP too although mitigated by the higher number of CLOSE states.
I believe there is a better solution than the one I proposed [simplest I could
think of].
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
---
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index f5ddd89..44c7de6 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -451,6 +451,7 @@ struct dccp_sock {
__u8 dccps_hc_tx_insert_options:1;
int dccps_l_ackr;
int dccps_r_ackr;
+ struct tasklet_struct dccps_delayed_ack;
};
static inline struct dccp_sock *dccp_sk(const struct sock *sk)
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 757539a..168f55b 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -261,6 +261,24 @@ discard:
EXPORT_SYMBOL_GPL(dccp_rcv_established);
+static void delayed_ack(unsigned long x) {
+ struct sock* sk;
+ struct dccp_sock *dp;
+
+ sk = (struct sock*) x;
+ dp = dccp_sk(sk);
+
+ bh_lock_sock(sk);
+ if (sk->sk_socket->state != SS_CONNECTING) {
+ dccp_send_ack(sk);
+ dp->dccps_delayed_ack.data = 0;
+ }
+ else {
+ tasklet_schedule(&dp->dccps_delayed_ack);
+ }
+ bh_unlock_sock(sk);
+}
+
static int dccp_rcv_request_sent_state_process(struct sock *sk,
struct sk_buff *skb,
const struct dccp_hdr *dh,
@@ -371,7 +389,25 @@ static int dccp_rcv_request_sent_state_p
__kfree_skb(skb);
return 0;
}
- dccp_send_ack(sk);
+
+ /* There is a race condition in which the connect system call
+ * returns after a short connection occured and finished. In
+ * such a case, connect will think the socket is closed and
+ * return -1. We prevent a backlog from occuring by checking
+ * that the connect returned here.
+ * -sorbo
+ */
+ if (sk->sk_socket->state = SS_CONNECTING) {
+ /* check whether we already scheduled tasklet */
+ if (!dp->dccps_delayed_ack.data) {
+ tasklet_init(&dp->dccps_delayed_ack,
+ &delayed_ack, (unsigned long) sk);
+ tasklet_schedule(&dp->dccps_delayed_ack);
+ } else {
+ BUG_TRAP(dp->dccps_delayed_ack.data =
+ (unsigned long) sk);
+ }
+ }
return -1;
}
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 4331cda..e5e874b 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -1101,6 +1101,7 @@ int dccp_v4_init_sock(struct sock *sk)
dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
dp->dccps_l_ackr = 0;
dp->dccps_r_ackr = 0;
+ dp->dccps_delayed_ack.data = 0;
return 0;
}
@@ -1136,6 +1137,9 @@ int dccp_v4_destroy_sock(struct sock *sk
ccid_exit(dp->dccps_hc_rx_ccid, sk);
ccid_exit(dp->dccps_hc_tx_ccid, sk);
dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
+
+ /* XXX need to kill tasklet if it's there! */
+ BUG_TRAP(!dp->dccps_delayed_ack.data);
return 0;
}
^ permalink raw reply related
* [PATCH 2/3] DCCP: Introduce CCID2
From: Andrea Bittau @ 2005-12-13 9:32 UTC (permalink / raw)
To: dccp
Initial support for CCID2 [TCP-like]. CCID2 is missing:
* ECN nonces.
* Detecting ECN marked ACKs.
* Determining when flows go silent [ack of acks business].
Support for changing the receivier and transmitter half-connection CCID is
available through setsockopt.
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
---
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index d0bdb49..f5ddd89 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -179,9 +179,14 @@ enum {
/* DCCP socket options */
#define DCCP_SOCKOPT_PACKET_SIZE 1
#define DCCP_SOCKOPT_SERVICE 2
+#define DCCP_SOCKOPT_CCID_RX 3
+#define DCCP_SOCKOPT_CCID_TX 4
#define DCCP_SOCKOPT_CCID_RX_INFO 128
#define DCCP_SOCKOPT_CCID_TX_INFO 192
+#define DCCP_CCID_TCP 2
+#define DCCP_CCID_TFRC 3
+
#define DCCP_SERVICE_LIST_MAX_LEN 32
#ifdef __KERNEL__
@@ -444,6 +449,8 @@ struct dccp_sock {
enum dccp_role dccps_role:2;
__u8 dccps_hc_rx_insert_options:1;
__u8 dccps_hc_tx_insert_options:1;
+ int dccps_l_ackr;
+ int dccps_r_ackr;
};
static inline struct dccp_sock *dccp_sk(const struct sock *sk)
diff --git a/net/dccp/ccids/Kconfig b/net/dccp/ccids/Kconfig
index 7684d83..8f0d66e 100644
--- a/net/dccp/ccids/Kconfig
+++ b/net/dccp/ccids/Kconfig
@@ -22,6 +22,14 @@ config IP_DCCP_CCID3
If in doubt, say M.
+config IP_DCCP_CCID2
+ tristate "CCID2 (TCP) (EXPERIMENTAL)"
+ depends on IP_DCCP && IP_DCCP_ACKVEC
+ ---help---
+ CCID 2 uses TCP-like congestion control.
+
+ If in doubt, say M.
+
config IP_DCCP_TFRC_LIB
depends on IP_DCCP_CCID3
def_tristate IP_DCCP_CCID3
diff --git a/net/dccp/ccids/Makefile b/net/dccp/ccids/Makefile
index 956f79f..438f20b 100644
--- a/net/dccp/ccids/Makefile
+++ b/net/dccp/ccids/Makefile
@@ -2,4 +2,8 @@ obj-$(CONFIG_IP_DCCP_CCID3) += dccp_ccid
dccp_ccid3-y := ccid3.o
+obj-$(CONFIG_IP_DCCP_CCID2) += dccp_ccid2.o
+
+dccp_ccid2-y := ccid2.o
+
obj-y += lib/
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
new file mode 100644
index 0000000..1bb532e
--- /dev/null
+++ b/net/dccp/ccids/ccid2.c
@@ -0,0 +1,942 @@
+/*
+ * net/dccp/ccids/ccid2.c
+ *
+ * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This implementation should follow: draft-ietf-dccp-ccid2-10.txt
+ *
+ * BUGS:
+ * - sequence number wrapping
+ * - jiffies wrapping
+ */
+
+#include <linux/config.h>
+#include "../ccid.h"
+#include "../dccp.h"
+#include "ccid2.h"
+
+static int ccid2_debug;
+
+#if 0
+#define CCID2_DEBUG
+#endif
+
+#ifdef CCID2_DEBUG
+#define ccid2_pr_debug(format, a...) \
+ do { if (ccid2_debug) \
+ printk(KERN_DEBUG "%s: " format, __FUNCTION__, ##a); \
+ } while (0)
+#else
+#define ccid2_pr_debug(format, a...)
+#endif
+
+static int _seq_len = 128;
+
+static struct ccid2_hc_tx_sock *ccid2_hc_tx_sk(struct sock *sk)
+{
+ return dccp_sk(sk)->dccps_hc_tx_ccid_private;
+}
+
+static struct ccid2_hc_rx_sock *ccid2_hc_rx_sk(struct sock *sk)
+{
+ return dccp_sk(sk)->dccps_hc_rx_ccid_private;
+}
+
+#ifdef CCID2_DEBUG
+static void check_sanity(struct ccid2_hc_tx_sock *hctx)
+{
+ int len = 0;
+ struct ccid2_seq *seqp;
+ int pipe = 0;
+
+ seqp = hctx->ccid2hctx_seqh;
+
+ /* there is data in the chain */
+ if (seqp != hctx->ccid2hctx_seqt) {
+ seqp = seqp->ccid2s_prev;
+ len++;
+ if (!seqp->ccid2s_acked)
+ pipe++;
+
+ while (seqp != hctx->ccid2hctx_seqt) {
+ struct ccid2_seq *prev;
+
+ prev = seqp->ccid2s_prev;
+ len++;
+ if (!prev->ccid2s_acked)
+ pipe++;
+
+ /* packets are sent sequentially */
+ BUG_ON(seqp->ccid2s_seq <= prev->ccid2s_seq);
+ BUG_ON(seqp->ccid2s_sent < prev->ccid2s_sent);
+ BUG_ON(len > _seq_len);
+
+ seqp = prev;
+ }
+ }
+
+ BUG_ON(pipe != hctx->ccid2hctx_pipe);
+ ccid2_pr_debug("len of chain=%d\n", len);
+
+ do {
+ seqp = seqp->ccid2s_prev;
+ len++;
+ BUG_ON(len > _seq_len);
+ } while(seqp != hctx->ccid2hctx_seqh);
+
+ BUG_ON(len != _seq_len);
+ ccid2_pr_debug("total len=%d\n", len);
+}
+#endif
+
+static int ccid2_init(struct sock *sk)
+{
+ return 0;
+}
+
+static void ccid2_exit(struct sock *sk)
+{
+}
+
+static int ccid2_hc_tx_send_packet(struct sock *sk,
+ struct sk_buff *skb, int len)
+{
+ struct ccid2_hc_tx_sock* hctx;
+
+ switch (DCCP_SKB_CB(skb)->dccpd_type) {
+ case 0: /* XXX data packets from userland come through like this */
+ case DCCP_PKT_DATA:
+ case DCCP_PKT_DATAACK:
+ break;
+
+ /* no congestion control on other packets */
+ default:
+ return 0;
+ }
+
+ hctx = ccid2_hc_tx_sk(sk);
+
+ ccid2_pr_debug("pipe=%d cwnd=%d\n", hctx->ccid2hctx_pipe,
+ hctx->ccid2hctx_cwnd);
+
+ if (hctx->ccid2hctx_pipe < hctx->ccid2hctx_cwnd) {
+ /* OK we can send... make sure previous packet was sent off */
+ if (!hctx->ccid2hctx_sendwait) {
+ hctx->ccid2hctx_sendwait = 1;
+ return 0;
+ }
+ }
+
+ return 100; /* XXX */
+}
+
+static inline void ccid2_change_l_ackr(struct sock *sk, int val)
+{
+ struct dccp_sock *dp;
+
+ dp = dccp_sk(sk);
+
+ /* XXX I don't really agree with val != 2. If cwnd is 1, ack ratio
+ * should be 1... it shouldn't be allowed to become 2.
+ * -sorbo.
+ */
+ if (val != 2) {
+ struct ccid2_hc_tx_sock *hctx;
+ int max = 0;
+
+ hctx = ccid2_hc_tx_sk(sk);
+
+ max = hctx->ccid2hctx_cwnd;
+ max >>= 1;
+
+ /* round up */
+ if (hctx->ccid2hctx_cwnd & 1)
+ max++;
+
+ if (val > max)
+ val = max;
+ }
+
+ ccid2_pr_debug("changing local ack ratio to %d\n", val);
+ BUG_ON(val <= 0);
+ dp->dccps_l_ackr = val;
+}
+
+static inline void ccid2_change_cwnd(struct sock *sk, int val)
+{
+ struct dccp_sock *dp;
+ struct ccid2_hc_tx_sock* hctx;
+
+ dp = dccp_sk(sk);
+ hctx = ccid2_hc_tx_sk(sk);
+
+ if (val = 0)
+ val = 1;
+
+ /* XXX do we need to change ack ratio? */
+ ccid2_pr_debug("change cwnd to %d\n", val);
+
+ BUG_ON(val < 1);
+ hctx->ccid2hctx_cwnd = val;
+}
+
+static void ccid2_hc_tx_rto_expire(unsigned long data)
+{
+ struct sock *sk;
+ struct ccid2_hc_tx_sock *hctx;
+ long s;
+
+ sk = (struct sock *) data;
+ hctx = ccid2_hc_tx_sk(sk);
+
+ /* XXX I don't think i'm locking correctly
+ * -sorbo.
+ */
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ /* XXX need to do something better I guess */
+ hctx->ccid2hctx_rtotimer.expires = jiffies + 200;
+ add_timer(&hctx->ccid2hctx_rtotimer);
+ goto out;
+ }
+
+ ccid2_pr_debug("RTO_EXPIRE\n");
+
+#ifdef CCID2_DEBUG
+ check_sanity(hctx);
+#endif
+
+ /* back-off timer */
+ hctx->ccid2hctx_rto <<= 1;
+
+ s = hctx->ccid2hctx_rto/HZ;
+ if (s > 60)
+ hctx->ccid2hctx_rto = 60*HZ;
+ ccid2_start_rto_timer(sk);
+
+ /* adjust pipe, cwnd etc */
+ hctx->ccid2hctx_pipe = 0;
+ hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd >> 1;
+ if (hctx->ccid2hctx_ssthresh < 2)
+ hctx->ccid2hctx_ssthresh = 2;
+ ccid2_change_cwnd(sk, 1);
+
+ /* clear state about stuff we sent */
+ hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqh;
+ hctx->ccid2hctx_ssacks = 0;
+ hctx->ccid2hctx_acks = 0;
+ hctx->ccid2hctx_sent = 0;
+
+ /* clear ack ratio state. */
+ hctx->ccid2hctx_arsent = 0;
+ hctx->ccid2hctx_ackloss = 0;
+ hctx->ccid2hctx_rpseq = 0;
+ hctx->ccid2hctx_rpdupack = -1;
+ ccid2_change_l_ackr(sk, 1);
+
+#ifdef CCID2_DEBUG
+ check_sanity(hctx);
+#endif
+out:
+ bh_unlock_sock(sk);
+/* sock_put(sk); */
+}
+
+static void ccid2_start_rto_timer(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx;
+
+ hctx = ccid2_hc_tx_sk(sk);
+ ccid2_pr_debug("setting RTO timeout=%ld\n", hctx->ccid2hctx_rto);
+
+ hctx->ccid2hctx_rtotimer.data = (unsigned long)sk;
+ hctx->ccid2hctx_rtotimer.function = &ccid2_hc_tx_rto_expire;
+ hctx->ccid2hctx_rtotimer.expires = jiffies + hctx->ccid2hctx_rto;
+
+ BUG_ON(timer_pending(&hctx->ccid2hctx_rtotimer));
+ add_timer(&hctx->ccid2hctx_rtotimer);
+}
+
+static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, int len)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct ccid2_hc_tx_sock* hctx;
+ u64 seq;
+
+ hctx = ccid2_hc_tx_sk(sk);
+
+#ifdef CCID2_DEBUG
+ check_sanity(hctx);
+#endif
+
+ BUG_ON(!hctx->ccid2hctx_sendwait);
+ hctx->ccid2hctx_sendwait = 0;
+ hctx->ccid2hctx_pipe++;
+ BUG_ON(hctx->ccid2hctx_pipe < 0);
+
+ /* There is an issue. What if another packet is sent between
+ * packet_send() and packet_sent(). Then the sequence number would be
+ * wrong.
+ * -sorbo.
+ */
+ seq = dp->dccps_gss;
+
+ hctx->ccid2hctx_seqh->ccid2s_seq = seq;
+ hctx->ccid2hctx_seqh->ccid2s_acked = 0;
+ hctx->ccid2hctx_seqh->ccid2s_sent = jiffies;
+ hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqh->ccid2s_next;
+
+ ccid2_pr_debug("cwnd=%d pipe=%d\n", hctx->ccid2hctx_cwnd,
+ hctx->ccid2hctx_pipe);
+
+ if (hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqt) {
+ /* XXX allocate more space */
+ BUG_ON(1);
+ }
+
+ hctx->ccid2hctx_sent++;
+
+ /* Ack Ratio. Need to maintain a concept of how many windows we sent */
+ hctx->ccid2hctx_arsent++;
+ /* We had an ack loss in this window... */
+ if (hctx->ccid2hctx_ackloss) {
+ if (hctx->ccid2hctx_arsent >= hctx->ccid2hctx_cwnd) {
+ hctx->ccid2hctx_arsent = 0;
+ hctx->ccid2hctx_ackloss = 0;
+ }
+ }
+ /* No acks lost up to now... */
+ else {
+ /* decrease ack ratio if enough packets were sent */
+ if (dp->dccps_l_ackr > 1) {
+ /* XXX don't calculate denominator each time */
+ int denom;
+
+ denom = dp->dccps_l_ackr*dp->dccps_l_ackr -
+ dp->dccps_l_ackr;
+ denom = hctx->ccid2hctx_cwnd*hctx->ccid2hctx_cwnd/denom;
+
+ if (hctx->ccid2hctx_arsent >= denom) {
+ ccid2_change_l_ackr(sk, dp->dccps_l_ackr-1);
+ hctx->ccid2hctx_arsent = 0;
+ }
+ }
+ /* we can't increase ack ratio further [1] */
+ else {
+ hctx->ccid2hctx_arsent = 0; /* or maybe set it to cwnd*/
+ }
+ }
+
+ /* setup RTO timer */
+ if (!timer_pending(&hctx->ccid2hctx_rtotimer)) {
+ ccid2_start_rto_timer(sk);
+ }
+#ifdef CCID2_DEBUG
+ ccid2_pr_debug("pipe=%d\n", hctx->ccid2hctx_pipe);
+ ccid2_pr_debug("Sent: seq=%llu\n", seq);
+ do {
+ struct ccid2_seq* seqp;
+
+ seqp = hctx->ccid2hctx_seqt;
+ while (seqp != hctx->ccid2hctx_seqh) {
+ ccid2_pr_debug("out seq=%llu acked=%d time=%lu\n",
+ seqp->ccid2s_seq, seqp->ccid2s_acked,
+ seqp->ccid2s_sent);
+ seqp = seqp->ccid2s_next;
+ }
+ } while(0);
+ ccid2_pr_debug("=====\n");
+ check_sanity(hctx);
+#endif
+}
+
+/* XXX Lame code duplication!
+ * returns -1 if none was found.
+ * else returns the next offset to use in the function call.
+ */
+static int ccid2_ackvector(struct sock *sk, struct sk_buff *skb, int offset,
+ unsigned char **vec, unsigned char *veclen)
+{
+ const struct dccp_hdr *dh = dccp_hdr(skb);
+ unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb);
+ unsigned char *opt_ptr;
+ const unsigned char *opt_end = (unsigned char *)dh +
+ (dh->dccph_doff * 4);
+ unsigned char opt, len;
+ unsigned char *value;
+
+ BUG_ON(offset < 0);
+ options += offset;
+ opt_ptr = options;
+ if (opt_ptr >= opt_end)
+ return -1;
+
+ while (opt_ptr != opt_end) {
+ opt = *opt_ptr++;
+ len = 0;
+ value = NULL;
+
+ /* Check if this isn't a single byte option */
+ if (opt > DCCPO_MAX_RESERVED) {
+ if (opt_ptr = opt_end)
+ goto out_invalid_option;
+
+ len = *opt_ptr++;
+ if (len < 3)
+ goto out_invalid_option;
+ /*
+ * Remove the type and len fields, leaving
+ * just the value size
+ */
+ len -= 2;
+ value = opt_ptr;
+ opt_ptr += len;
+
+ if (opt_ptr > opt_end)
+ goto out_invalid_option;
+ }
+
+ switch (opt) {
+ case DCCPO_ACK_VECTOR_0:
+ case DCCPO_ACK_VECTOR_1:
+ *vec = value;
+ *veclen = len;
+ return offset + (opt_ptr - options);
+ break;
+ }
+ }
+
+ return -1;
+
+out_invalid_option:
+ BUG_ON(1); /* should never happen... options were previously parsed ! */
+ return -1;
+}
+
+static void ccid2_hc_tx_kill_rto_timer(struct ccid2_hc_tx_sock *hctx)
+{
+ if (timer_pending(&hctx->ccid2hctx_rtotimer)) {
+ ccid2_pr_debug("deleting RTO timer\n");
+ del_timer(&hctx->ccid2hctx_rtotimer);
+ }
+}
+
+static inline void ccid2_new_ack(struct sock *sk,
+ struct ccid2_seq *seqp,
+ unsigned int *maxincr)
+{
+ struct ccid2_hc_tx_sock *hctx;
+
+ hctx = ccid2_hc_tx_sk(sk);
+
+ /* slow start */
+ if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh) {
+ hctx->ccid2hctx_acks = 0;
+
+ /* We can increase cwnd at most maxincr [ackratio/2] */
+ if (*maxincr) {
+ /* increase every 2 acks */
+ hctx->ccid2hctx_ssacks++;
+ if (hctx->ccid2hctx_ssacks = 2) {
+ ccid2_change_cwnd(sk, hctx->ccid2hctx_cwnd + 1);
+ hctx->ccid2hctx_ssacks = 0;
+ *maxincr = *maxincr - 1;
+ }
+ }
+ /* increased cwnd enough for this single ack */
+ else {
+ hctx->ccid2hctx_ssacks = 0;
+ }
+ }
+ else {
+ hctx->ccid2hctx_ssacks = 0;
+ hctx->ccid2hctx_acks++;
+
+ if (hctx->ccid2hctx_acks >= hctx->ccid2hctx_cwnd) {
+ ccid2_change_cwnd(sk, hctx->ccid2hctx_cwnd + 1);
+ hctx->ccid2hctx_acks = 0;
+ }
+ }
+
+ /* update RTO */
+ if (hctx->ccid2hctx_srtt = -1 ||
+ (jiffies - hctx->ccid2hctx_lastrtt) >= hctx->ccid2hctx_srtt) {
+ unsigned long r = jiffies - seqp->ccid2s_sent;
+ int s;
+
+ /* first measurement */
+ if (hctx->ccid2hctx_srtt = -1) {
+ ccid2_pr_debug("R: %lu Time=%lu seq=%llu\n",
+ r, jiffies, seqp->ccid2s_seq);
+ hctx->ccid2hctx_srtt = r;
+ hctx->ccid2hctx_rttvar = r >> 1;
+ }
+ else {
+ /* RTTVAR */
+ long tmp = hctx->ccid2hctx_srtt - r;
+ if (tmp < 0)
+ tmp *= -1;
+
+ tmp >>= 2;
+ hctx->ccid2hctx_rttvar *= 3;
+ hctx->ccid2hctx_rttvar >>= 2;
+ hctx->ccid2hctx_rttvar += tmp;
+
+ /* SRTT */
+ hctx->ccid2hctx_srtt *= 7;
+ hctx->ccid2hctx_srtt >>= 3;
+ tmp = r >> 3;
+ hctx->ccid2hctx_srtt += tmp;
+ }
+ s = hctx->ccid2hctx_rttvar << 2;
+ /* clock granularity is 1 when based on jiffies */
+ if (!s)
+ s = 1;
+ hctx->ccid2hctx_rto = hctx->ccid2hctx_srtt + s;
+
+ /* must be at least a second */
+ s = hctx->ccid2hctx_rto/HZ;
+ /* DCCP doesn't require this [but I like it cuz my code sux] */
+#if 1
+ if (s < 1)
+ hctx->ccid2hctx_rto = HZ;
+#endif
+ /* max 60 seconds */
+ if (s > 60)
+ hctx->ccid2hctx_rto = HZ*60;
+
+ hctx->ccid2hctx_lastrtt = jiffies;
+
+ ccid2_pr_debug("srtt: %ld rttvar: %ld rto: %ld (HZ=%d) R=%lu\n",
+ hctx->ccid2hctx_srtt, hctx->ccid2hctx_rttvar,
+ hctx->ccid2hctx_rto, HZ, r);
+ hctx->ccid2hctx_sent = 0;
+ }
+
+ /* we got a new ack, so re-start RTO timer */
+ ccid2_hc_tx_kill_rto_timer(hctx);
+ ccid2_start_rto_timer(sk);
+}
+
+static void ccid2_hc_tx_dec_pipe(struct ccid2_hc_tx_sock *hctx)
+{
+ hctx->ccid2hctx_pipe--;
+ BUG_ON(hctx->ccid2hctx_pipe < 0);
+
+ if (hctx->ccid2hctx_pipe = 0)
+ ccid2_hc_tx_kill_rto_timer(hctx);
+}
+
+static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
+{
+ struct dccp_sock *dp;
+ struct ccid2_hc_tx_sock *hctx;
+ u64 ackno, seqno;
+ struct ccid2_seq *seqp;
+ unsigned char *vector;
+ unsigned char veclen;
+ int offset = 0;
+ int done = 0;
+ int loss = 0;
+ unsigned int maxincr = 0;
+
+ dp = dccp_sk(sk);
+ hctx = ccid2_hc_tx_sk(sk);
+
+#ifdef CCID2_DEBUG
+ check_sanity(hctx);
+#endif
+ /* check reverse path congestion */
+ seqno = DCCP_SKB_CB(skb)->dccpd_seq;
+
+ /* XXX this whole "algorithm" is broken. Need to fix it to keep track
+ * of the seqnos of the dupacks so that rpseq and rpdupack are correct
+ * -sorbo.
+ */
+ /* need to bootstrap */
+ if (hctx->ccid2hctx_rpdupack = -1) {
+ hctx->ccid2hctx_rpdupack = 0;
+ hctx->ccid2hctx_rpseq = seqno;
+ }
+ else {
+ /* check if packet is consecutive */
+ if ((hctx->ccid2hctx_rpseq + 1) = seqno) {
+ hctx->ccid2hctx_rpseq++;
+ }
+ /* it's a later packet */
+ else if (after48(seqno, hctx->ccid2hctx_rpseq)) {
+ hctx->ccid2hctx_rpdupack++;
+
+ /* check if we got enough dupacks */
+ if (hctx->ccid2hctx_rpdupack >+ hctx->ccid2hctx_numdupack) {
+
+ hctx->ccid2hctx_rpdupack = -1; /* XXX lame */
+ hctx->ccid2hctx_rpseq = 0;
+
+ ccid2_change_l_ackr(sk, dp->dccps_l_ackr << 1);
+ }
+ }
+ }
+
+ /* check forward path congestion */
+ /* still didn't send out new data packets */
+ if (hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqt)
+ return;
+
+ switch (DCCP_SKB_CB(skb)->dccpd_type) {
+ case DCCP_PKT_ACK:
+ case DCCP_PKT_DATAACK:
+ break;
+
+ default:
+ return;
+ }
+
+ ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
+ seqp = hctx->ccid2hctx_seqh->ccid2s_prev;
+
+ /* If in slow-start, cwnd can increase at most Ack Ratio / 2 packets for
+ * this single ack. I round up.
+ * -sorbo.
+ */
+ maxincr = dp->dccps_l_ackr >> 1;
+ maxincr++;
+
+ /* go through all ack vectors */
+ while ( (offset = ccid2_ackvector(sk, skb, offset,
+ &vector, &veclen)) != -1) {
+
+ /* go through this ack vector */
+ while (veclen--) {
+ const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK;
+ u64 ackno_end_rl;
+
+ dccp_set_seqno(&ackno_end_rl, ackno - rl);
+ ccid2_pr_debug("ackvec start:%llu end:%llu\n", ackno,
+ ackno_end_rl);
+ /* if the seqno we are analyzing is larger than the
+ * current ackno, then move towards the tail of our
+ * seqnos.
+ */
+ while (after48(seqp->ccid2s_seq, ackno)) {
+ if (seqp = hctx->ccid2hctx_seqt) {
+ done = 1;
+ break;
+ }
+ seqp = seqp->ccid2s_prev;
+ }
+ if (done)
+ break;
+
+ /* check all seqnos in the range of the vector
+ * run length
+ */
+ while (between48(seqp->ccid2s_seq,ackno_end_rl,ackno)) {
+ const u8 state = (*vector &
+ DCCP_ACKVEC_STATE_MASK) >> 6;
+
+ /* new packet received or marked */
+ if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED &&
+ !seqp->ccid2s_acked) {
+ if (state =
+ DCCP_ACKVEC_STATE_ECN_MARKED) {
+ loss = 1;
+ }
+ else {
+ ccid2_new_ack(sk, seqp,
+ &maxincr);
+ }
+
+ seqp->ccid2s_acked = 1;
+ ccid2_pr_debug("Got ack for %llu\n",
+ seqp->ccid2s_seq);
+ ccid2_hc_tx_dec_pipe(hctx);
+ }
+ if (seqp = hctx->ccid2hctx_seqt) {
+ done = 1;
+ break;
+ }
+ seqp = seqp->ccid2s_next;
+ }
+ if (done)
+ break;
+
+
+ dccp_set_seqno(&ackno, ackno_end_rl - 1);
+ vector++;
+ }
+ if (done)
+ break;
+ }
+
+ /* The state about what is acked should be correct now
+ * Check for NUMDUPACK
+ */
+ seqp = hctx->ccid2hctx_seqh->ccid2s_prev;
+ done = 0;
+ while (1) {
+ if (seqp->ccid2s_acked) {
+ done++;
+ if (done = hctx->ccid2hctx_numdupack) {
+ break;
+ }
+ }
+ if (seqp = hctx->ccid2hctx_seqt) {
+ break;
+ }
+ seqp = seqp->ccid2s_prev;
+ }
+
+ /* If there are at least 3 acknowledgements, anything unacknowledged
+ * below the last sequence number is considered lost
+ */
+ if (done = hctx->ccid2hctx_numdupack) {
+ struct ccid2_seq *last_acked = seqp;
+
+ /* check for lost packets */
+ while (1) {
+ if (!seqp->ccid2s_acked) {
+ loss = 1;
+ ccid2_hc_tx_dec_pipe(hctx);
+ }
+ if (seqp = hctx->ccid2hctx_seqt)
+ break;
+ seqp = seqp->ccid2s_prev;
+ }
+
+ hctx->ccid2hctx_seqt = last_acked;
+ }
+
+ /* trim acked packets in tail */
+ while (hctx->ccid2hctx_seqt != hctx->ccid2hctx_seqh) {
+ if (!hctx->ccid2hctx_seqt->ccid2s_acked)
+ break;
+
+ hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqt->ccid2s_next;
+ }
+
+ if (loss) {
+ /* XXX do bit shifts guarantee a 0 as the new bit? */
+ ccid2_change_cwnd(sk, hctx->ccid2hctx_cwnd >> 1);
+ hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd;
+ if (hctx->ccid2hctx_ssthresh < 2)
+ hctx->ccid2hctx_ssthresh = 2;
+ }
+
+#ifdef CCID2_DEBUG
+ check_sanity(hctx);
+#endif
+}
+
+static void ccid2_hc_tx_insert_options(struct sock *sk, struct sk_buff *skb)
+{
+}
+
+static int ccid2_hc_tx_parse_options(struct sock *sk, unsigned char option,
+ unsigned char len, u16 idx,
+ unsigned char *value)
+{
+ return 0;
+}
+
+static int ccid2_hc_tx_init(struct sock *sk)
+{
+ struct dccp_sock *dp;
+ struct ccid2_hc_tx_sock *hctx;
+ int seqcount = _seq_len;
+ int i;
+
+ dp = dccp_sk(sk);
+ dp->dccps_hc_tx_ccid_private = kmalloc(sizeof(*hctx), gfp_any());
+ if (dp->dccps_hc_tx_ccid_private = NULL)
+ return -ENOMEM;
+
+ hctx = ccid2_hc_tx_sk(sk);
+ memset(hctx, 0, sizeof(*hctx));
+
+ /* XXX init variables with proper values */
+ hctx->ccid2hctx_cwnd = 1;
+ hctx->ccid2hctx_ssthresh = 10;
+
+ hctx->ccid2hctx_ssacks = 0;
+ hctx->ccid2hctx_acks = 0;
+ hctx->ccid2hctx_numdupack = 3;
+
+ /* XXX init ~ to window size... */
+ hctx->ccid2hctx_seqbuf = kmalloc(sizeof(*hctx->ccid2hctx_seqbuf)*
+ seqcount, gfp_any());
+ if (hctx->ccid2hctx_seqbuf = NULL) {
+ kfree(dp->dccps_hc_tx_ccid_private);
+ dp->dccps_hc_tx_ccid_private = NULL;
+ return -ENOMEM;
+ }
+ for (i = 0; i < (seqcount-1); i++) {
+ hctx->ccid2hctx_seqbuf[i].ccid2s_next =
+ &hctx->ccid2hctx_seqbuf[i+1];
+ hctx->ccid2hctx_seqbuf[i+1].ccid2s_prev =
+ &hctx->ccid2hctx_seqbuf[i];
+ }
+ hctx->ccid2hctx_seqbuf[seqcount-1].ccid2s_next = hctx->ccid2hctx_seqbuf;
+ hctx->ccid2hctx_seqbuf->ccid2s_prev =
+ &hctx->ccid2hctx_seqbuf[seqcount-1];
+
+ hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqbuf;
+ hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqh;
+
+ hctx->ccid2hctx_sent = 0;
+ hctx->ccid2hctx_rto = 3*HZ;
+ hctx->ccid2hctx_srtt = -1;
+ hctx->ccid2hctx_rttvar = -1;
+ hctx->ccid2hctx_lastrtt = 0;
+ hctx->ccid2hctx_rpdupack = -1;
+
+ init_timer(&hctx->ccid2hctx_rtotimer);
+
+#ifdef CCID2_DEBUG
+ check_sanity(hctx);
+#endif
+ return 0;
+}
+
+static void ccid2_hc_tx_exit(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx;
+ struct dccp_sock *dp = dccp_sk(sk);
+
+ hctx = dp->dccps_hc_tx_ccid_private;
+
+ ccid2_hc_tx_kill_rto_timer(hctx);
+
+ kfree(hctx->ccid2hctx_seqbuf);
+
+ kfree(dp->dccps_hc_tx_ccid_private);
+ dp->dccps_hc_tx_ccid_private = NULL;
+}
+
+static void ccid2_hc_rx_insert_options(struct sock *sk, struct sk_buff *skb)
+{
+}
+
+static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
+{
+ struct dccp_sock *dp;
+ struct ccid2_hc_rx_sock *hcrx;
+
+ switch (DCCP_SKB_CB(skb)->dccpd_type) {
+ case DCCP_PKT_DATA:
+ case DCCP_PKT_DATAACK:
+ dp = dccp_sk(sk);
+ hcrx = ccid2_hc_rx_sk(sk);
+
+ hcrx->ccid2hcrx_data++;
+ if (hcrx->ccid2hcrx_data >= dp->dccps_r_ackr) {
+ dccp_send_ack(sk);
+ hcrx->ccid2hcrx_data = 0;
+ }
+ break;
+ }
+}
+
+static int ccid2_hc_rx_init(struct sock *sk)
+{
+ struct dccp_sock *dp;
+ struct ccid2_hc_rx_sock *hcrx;
+
+ dp = dccp_sk(sk);
+ dp->dccps_hc_rx_ccid_private = kmalloc(sizeof(*hcrx), gfp_any());
+ if (dp->dccps_hc_rx_ccid_private = NULL)
+ return -ENOMEM;
+
+ hcrx = ccid2_hc_rx_sk(sk);
+ memset(hcrx, 0, sizeof(*hcrx));
+
+ return 0;
+}
+
+static void ccid2_hc_rx_exit(struct sock *sk)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+
+ kfree(dp->dccps_hc_rx_ccid_private);
+ dp->dccps_hc_rx_ccid_private = NULL;
+}
+
+static void ccid2_hc_rx_get_info(struct sock *sk, struct tcp_info *info)
+{
+}
+
+static void ccid2_hc_tx_get_info(struct sock *sk, struct tcp_info *info)
+{
+}
+
+static int ccid2_hc_rx_getsockopt(struct sock *sk, const int optname, int len,
+ u32 __user *optval, int __user *optlen)
+{
+ return -ENOPROTOOPT;
+}
+
+static int ccid2_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
+ u32 __user *optval, int __user *optlen)
+{
+ return -ENOPROTOOPT;
+}
+
+static struct ccid ccid2 = {
+ .ccid_id = 2,
+ .ccid_name = "ccid2",
+ .ccid_owner = THIS_MODULE,
+ .ccid_init = ccid2_init,
+ .ccid_exit = ccid2_exit,
+ .ccid_hc_tx_init = ccid2_hc_tx_init,
+ .ccid_hc_tx_exit = ccid2_hc_tx_exit,
+ .ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet,
+ .ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent,
+ .ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv,
+ .ccid_hc_tx_insert_options = ccid2_hc_tx_insert_options,
+ .ccid_hc_tx_parse_options = ccid2_hc_tx_parse_options,
+ .ccid_hc_rx_init = ccid2_hc_rx_init,
+ .ccid_hc_rx_exit = ccid2_hc_rx_exit,
+ .ccid_hc_rx_insert_options = ccid2_hc_rx_insert_options,
+ .ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv,
+ .ccid_hc_rx_get_info = ccid2_hc_rx_get_info,
+ .ccid_hc_tx_get_info = ccid2_hc_tx_get_info,
+ .ccid_hc_rx_getsockopt = ccid2_hc_rx_getsockopt,
+ .ccid_hc_tx_getsockopt = ccid2_hc_tx_getsockopt,
+};
+
+module_param(ccid2_debug, int, 0444);
+MODULE_PARM_DESC(ccid2_debug, "Enable debug messages");
+
+static __init int ccid2_module_init(void)
+{
+ return ccid_register(&ccid2);
+}
+module_init(ccid2_module_init);
+
+static __exit void ccid2_module_exit(void)
+{
+ ccid_unregister(&ccid2);
+}
+module_exit(ccid2_module_exit);
+
+MODULE_AUTHOR("Andrea Bittau <a.bittau@cs.ucl.ac.uk>");
+MODULE_DESCRIPTION("DCCP TCP CCID2 CCID");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("net-dccp-ccid-2");
diff --git a/net/dccp/ccids/ccid2.h b/net/dccp/ccids/ccid2.h
new file mode 100644
index 0000000..9ae07f9
--- /dev/null
+++ b/net/dccp/ccids/ccid2.h
@@ -0,0 +1,60 @@
+/*
+ * net/dccp/ccids/ccid2.h
+ *
+ * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _DCCP_CCID2_H_
+#define _DCCP_CCID2_H_
+
+struct ccid2_seq {
+ u64 ccid2s_seq;
+ int ccid2s_acked;
+ unsigned long ccid2s_sent;
+ struct ccid2_seq *ccid2s_prev;
+ struct ccid2_seq *ccid2s_next;
+};
+
+struct ccid2_hc_tx_sock {
+ int ccid2hctx_cwnd;
+ int ccid2hctx_ssacks; /* ACKs recv in slow start */
+ int ccid2hctx_acks; /* ACKS recv in AI phase */
+ int ccid2hctx_ssthresh;
+ int ccid2hctx_pipe;
+ int ccid2hctx_numdupack;
+ struct ccid2_seq *ccid2hctx_seqbuf;
+ struct ccid2_seq *ccid2hctx_seqh;
+ struct ccid2_seq *ccid2hctx_seqt;
+ long ccid2hctx_rto;
+ long ccid2hctx_srtt;
+ long ccid2hctx_rttvar;
+ int ccid2hctx_sent;/* packets sent in this window */
+ unsigned long ccid2hctx_lastrtt;/*time RTT was last measured*/
+ struct timer_list ccid2hctx_rtotimer;
+ unsigned long ccid2hctx_arsent; /* packets sent [ack ratio] */
+ int ccid2hctx_ackloss; /* ack was lost in this win*/
+ u64 ccid2hctx_rpseq; /* last consecutive seqno */
+ int ccid2hctx_rpdupack; /*dupacks since rpseq */
+ int ccid2hctx_sendwait;
+};
+
+struct ccid2_hc_rx_sock {
+ int ccid2hcrx_data;
+};
+
+static void ccid2_start_rto_timer(struct sock *sk);
+
+#endif /* _DCCP_CCID2_H_ */
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 5fdad99..4331cda 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -1099,6 +1099,8 @@ int dccp_v4_init_sock(struct sock *sk)
dp->dccps_mss_cache = 536;
dp->dccps_role = DCCP_ROLE_UNDEFINED;
dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
+ dp->dccps_l_ackr = 0;
+ dp->dccps_r_ackr = 0;
return 0;
}
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 40a4c68..236da8c 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -256,6 +256,82 @@ static int dccp_setsockopt_service(struc
return 0;
}
+static int dccp_setsockopt_ccid(struct sock *sk, const u32 ccid, int rx)
+{
+ struct ccid *c;
+ struct dccp_sock *dp = dccp_sk(sk);
+
+ c = ccid_init(ccid, sk);
+
+ if (c = NULL) {
+ ccid_exit(c, sk);
+ return -1;
+ }
+
+ if (rx) {
+ ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk);
+ ccid_exit(dp->dccps_hc_rx_ccid, sk);
+ dp->dccps_hc_rx_ccid = c;
+ }
+ else {
+ ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk);
+ ccid_exit(dp->dccps_hc_tx_ccid, sk);
+ dp->dccps_hc_tx_ccid = c;
+ }
+ return 0;
+}
+
+static int dccp_setsockopt_ccid_rx(struct sock *sk, const u32 ccid)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ int err = -EINVAL;
+
+ lock_sock(sk);
+
+ if (dccp_setsockopt_ccid(sk, ccid, 1))
+ goto out;
+ err = 0;
+
+ switch (ccid) {
+ case DCCP_CCID_TCP:
+ if (!dp->dccps_options.dccpo_send_ack_vector) {
+ dp->dccps_options.dccpo_send_ack_vector = 1;
+ dp->dccps_hc_rx_ackvec =
+ dccp_ackvec_alloc(DCCP_MAX_ACKVEC_LEN, GFP_ATOMIC);
+ if (dp->dccps_hc_rx_ackvec = NULL)
+ err = -ENOMEM;
+ }
+ dp->dccps_l_ackr = 1;
+ break;
+ }
+
+out:
+ release_sock(sk);
+ return err;
+}
+
+static int dccp_setsockopt_ccid_tx(struct sock *sk, const u32 ccid)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ int err = -EINVAL;
+
+ lock_sock(sk);
+
+ if (dccp_setsockopt_ccid(sk, ccid, 0))
+ goto out;
+ err = 0;
+
+ switch (ccid) {
+ case DCCP_CCID_TCP:
+ dp->dccps_r_ackr = 1;
+ break;
+ }
+
+out:
+ release_sock(sk);
+ return err;
+}
+
int dccp_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen)
{
@@ -274,8 +350,16 @@ int dccp_setsockopt(struct sock *sk, int
if (get_user(val, (int __user *)optval))
return -EFAULT;
- if (optname = DCCP_SOCKOPT_SERVICE)
+ switch (optname) {
+ case DCCP_SOCKOPT_SERVICE:
return dccp_setsockopt_service(sk, val, optval, optlen);
+
+ case DCCP_SOCKOPT_CCID_RX:
+ return dccp_setsockopt_ccid_rx(sk, val);
+
+ case DCCP_SOCKOPT_CCID_TX:
+ return dccp_setsockopt_ccid_tx(sk, val);
+ }
lock_sock(sk);
dp = dccp_sk(sk);
^ permalink raw reply related
* [PATCH 1/3] DCCP: Introduce ACK vector records
From: Andrea Bittau @ 2005-12-13 9:31 UTC (permalink / raw)
To: dccp
Initial support for ACK vector records. Currently there is a fixed number of
records which doesn't grow at run-time. Need to decide what to do [currently
dies] when we run out of records [allocate more, return -1?].
Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk>
---
diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig
index 187ac18..f39abaf 100644
--- a/net/dccp/Kconfig
+++ b/net/dccp/Kconfig
@@ -36,6 +36,13 @@ config IP_DCCP_DEBUG
Just say N.
+config IP_DCCP_ACKVEC
+ bool "DCCP ACK vector"
+ ---help---
+ Enable the use of ACK vectors.
+
+ Just say N.
+
config IP_DCCP_UNLOAD_HACK
depends on IP_DCCP=m && IP_DCCP_CCID3=m
bool "DCCP control sock unload hack"
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
index c9a62cc..12a1fa5 100644
--- a/net/dccp/ackvec.c
+++ b/net/dccp/ackvec.c
@@ -17,15 +17,84 @@
#include <net/sock.h>
+static void dccp_ackvec_insert_avr(struct dccp_ackvec *av,
+ struct dccp_ackvec_record *avr)
+{
+ struct dccp_ackvec_record *pos, *prev;
+
+ BUG_ON(av = NULL);
+ BUG_ON(avr = NULL);
+
+#if CONFIG_IP_DCCP_DEBUG
+ dccp_pr_debug("SEQ=%llu %p\n", avr->dccpavr_ack_seqno, avr);
+ pos = av->dccpav_record;
+
+ while (pos) {
+ dccp_pr_debug("Printing seq=%llu %p\n", pos->dccpavr_ack_seqno,
+ pos);
+ pos = pos->dccpavr_next;
+ }
+#endif
+ pos = av->dccpav_record;
+
+ /* first record */
+ if (!pos) {
+ avr->dccpavr_next = avr->dccpavr_prev = NULL;
+ av->dccpav_record = avr;
+ return;
+ }
+
+ /* find where to insert record... ordered by seqno */
+ while (pos) {
+ prev = pos;
+ if (avr->dccpavr_ack_seqno > pos->dccpavr_ack_seqno)
+ break;
+ pos = pos->dccpavr_next;
+ }
+
+ /* last record */
+ if (!pos) {
+ BUG_ON(prev = NULL);
+
+ prev->dccpavr_next = avr;
+ avr->dccpavr_prev = prev;
+ avr->dccpavr_next = NULL;
+ return;
+ }
+
+ /* in the middle somewhere or head */
+ avr->dccpavr_next = pos;
+ avr->dccpavr_prev = pos->dccpavr_prev;
+ pos->dccpavr_prev = avr;
+
+ /* middle */
+ if (avr->dccpavr_prev) {
+ BUG_ON(avr->dccpavr_prev = NULL);
+
+ avr->dccpavr_prev->dccpavr_next = avr;
+ }
+ /* head */
+ else {
+ av->dccpav_record = avr;
+ }
+}
+
int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec;
- int len = av->dccpav_vec_len + 2;
+ int len;
struct timeval now;
u32 elapsed_time;
unsigned char *to, *from;
+ struct dccp_ackvec_record *avr;
+#ifdef CONFIG_IP_DCCP_DEBUG
+ const char *debug_prefix = dp->dccps_role = DCCP_ROLE_CLIENT ?
+ "CLIENT rx ack: " : "server rx ack: ";
+#endif
+ BUG_ON(av = NULL);
+ len = av->dccpav_vec_len + 2;
dccp_timestamp(sk, &now);
elapsed_time = timeval_delta(&now, &av->dccpav_time) / 10;
@@ -35,19 +104,24 @@ int dccp_insert_option_ackvec(struct soc
if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
return -1;
- /*
- * XXX: now we have just one ack vector sent record, so
- * we have to wait for it to be cleared.
- *
- * Of course this is not acceptable, but this is just for
- * basic testing now.
+ /* We don't have space to store ACK vector.
+ * Two options:
+ * 1) Allocate more space.
+ * 2) Error.
+ * We do 2 because it's easy =D
+ * -sorbo.
*/
- if (av->dccpav_ack_seqno != DCCP_MAX_SEQNO + 1)
+ if (av->dccpav_record_pool_free = av->dccpav_record_pool_len) {
+ BUG_ON(1);
return -1;
+ }
+ avr = av->dccpav_record_pool[av->dccpav_record_pool_free++];
+ BUG_ON(avr = NULL);
DCCP_SKB_CB(skb)->dccpd_opt_len += len;
to = skb_push(skb, len);
+ BUG_ON(to = NULL);
*to++ = DCCPO_ACK_VECTOR_0;
*to++ = len;
@@ -55,8 +129,8 @@ int dccp_insert_option_ackvec(struct soc
from = av->dccpav_buf + av->dccpav_buf_head;
/* Check if buf_head wraps */
- if (av->dccpav_buf_head + len > av->dccpav_vec_len) {
- const u32 tailsize = (av->dccpav_vec_len - av->dccpav_buf_head);
+ if (av->dccpav_buf_head + len > av->dccpav_buf_len) {
+ const u32 tailsize = (av->dccpav_buf_len - av->dccpav_buf_head);
memcpy(to, from, tailsize);
to += tailsize;
@@ -76,26 +150,37 @@ int dccp_insert_option_ackvec(struct soc
*
* This implemention uses just one ack record for now.
*/
- av->dccpav_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq;
- av->dccpav_ack_ptr = av->dccpav_buf_head;
- av->dccpav_ack_ackno = av->dccpav_buf_ackno;
- av->dccpav_ack_nonce = av->dccpav_buf_nonce;
- av->dccpav_sent_len = av->dccpav_vec_len;
+ avr->dccpavr_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq;
+ avr->dccpavr_ack_ptr = av->dccpav_buf_head;
+ avr->dccpavr_ack_ackno = av->dccpav_buf_ackno;
+ avr->dccpavr_ack_nonce = av->dccpav_buf_nonce;
+ avr->dccpavr_sent_len = av->dccpav_vec_len;
+ dccp_ackvec_insert_avr(av, avr);
dccp_pr_debug("%sACK Vector 0, len=%d, ack_seqno=%llu, "
"ack_ackno=%llu\n",
- debug_prefix, av->dccpav_sent_len,
- (unsigned long long)av->dccpav_ack_seqno,
- (unsigned long long)av->dccpav_ack_ackno);
- return -1;
+ debug_prefix, avr->dccpavr_sent_len,
+ (unsigned long long)avr->dccpavr_ack_seqno,
+ (unsigned long long)avr->dccpavr_ack_ackno);
+
+ return 0;
}
struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len,
const gfp_t priority)
{
+ struct dccp_ackvec_record *avr;
struct dccp_ackvec *av = kmalloc(sizeof(*av) + len, priority);
if (av != NULL) {
+ /* Initial pool of records. We should allow for this pool to
+ * grow at run-time or just put a maximum value for it...
+ * For now, it's a maximum value.
+ * -sorbo.
+ */
+ unsigned int records = 128;
+ unsigned int i;
+
av->dccpav_buf_len = len;
av->dccpav_buf_head av->dccpav_buf_tail = av->dccpav_buf_len - 1;
@@ -106,13 +191,40 @@ struct dccp_ackvec *dccp_ackvec_alloc(co
av->dccpav_time.tv_sec = 0;
av->dccpav_time.tv_usec = 0;
av->dccpav_sent_len = av->dccpav_vec_len = 0;
- }
+ av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
+ av->dccpav_record_pool = kmalloc(
+ sizeof(*av->dccpav_record_pool) * records, priority);
+ if (av->dccpav_record_pool = NULL)
+ goto fail_rec_pool;
+
+ avr = kmalloc(sizeof(*avr) * records, priority);
+ if (avr = NULL)
+ goto fail_avr;
+
+ av->dccpav_record = NULL;
+ av->dccpav_record_pool_len = records;
+ /* position 0 contains original pointer, so we can free it */
+ av->dccpav_record_pool_free = 1;
+ for (i = 0; i < av->dccpav_record_pool_len; i++)
+ av->dccpav_record_pool[i] = avr++;
+ }
+out:
return av;
+
+fail_avr:
+ kfree(av->dccpav_record_pool);
+
+fail_rec_pool:
+ kfree(av);
+ av = NULL;
+ goto out;
}
void dccp_ackvec_free(struct dccp_ackvec *av)
{
+ kfree(av->dccpav_record_pool[0]);
+ kfree(av->dccpav_record_pool);
kfree(av);
}
@@ -291,43 +403,61 @@ void dccp_ackvec_print(const struct dccp
}
#endif
-static void dccp_ackvec_trow_away_ack_record(struct dccp_ackvec *av)
+static void dccp_ackvec_throw_away_ack_record(struct dccp_ackvec *av,
+ struct dccp_ackvec_record *avr)
{
- /*
- * As we're keeping track of the ack vector size (dccpav_vec_len) and
- * the sent ack vector size (dccpav_sent_len) we don't need
- * dccpav_buf_tail at all, but keep this code here as in the future
- * we'll implement a vector of ack records, as suggested in
- * draft-ietf-dccp-spec-11.txt Appendix A. -acme
+ /* In appendix A of the spec, I think that ack_ptr + 1 means "ack_ptr
+ * one slot to the left". I.e. we already acked ACK_PTR so we only
+ * wish to ack new stuff in the future. Thus ack_ptr - 1 in this code.
+ * -sorbo.
*/
-#if 0
- av->dccpav_buf_tail = av->dccpav_ack_ptr + 1;
- if (av->dccpav_buf_tail >= av->dccpav_vec_len)
- av->dccpav_buf_tail -= av->dccpav_vec_len;
-#endif
- av->dccpav_vec_len -= av->dccpav_sent_len;
+ /* XXX: we don't use buf_tail but update it anyway... */
+ av->dccpav_buf_tail = avr->dccpavr_ack_ptr;
+ if (!av->dccpav_buf_tail)
+ av->dccpav_buf_tail = av->dccpav_buf_len;
+ av->dccpav_buf_tail--;
+
+ av->dccpav_vec_len -= avr->dccpavr_sent_len;
+
+ /* free records */
+ dccp_pr_debug("free avr: %llu %p\n", avr->dccpavr_ack_seqno, avr);
+
+ if (avr->dccpavr_prev)
+ avr->dccpavr_prev->dccpavr_next = NULL;
+ else
+ av->dccpav_record = NULL;
+ while (avr) {
+ av->dccpav_record_pool_free--;
+ BUG_ON(av->dccpav_record_pool_free = 0);
+
+ av->dccpav_record_pool[av->dccpav_record_pool_free] = avr;
+ avr = avr->dccpavr_next;
+ }
}
void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk,
const u64 ackno)
{
- /* Check if we actually sent an ACK vector */
- if (av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1)
- return;
-
- if (ackno = av->dccpav_ack_seqno) {
+ struct dccp_ackvec_record *avr;
+
+ avr = av->dccpav_record;
+ while (avr) {
+ if (ackno = avr->dccpavr_ack_seqno) {
#ifdef CONFIG_IP_DCCP_DEBUG
- struct dccp_sock *dp = dccp_sk(sk);
- const char *debug_prefix = dp->dccps_role = DCCP_ROLE_CLIENT ?
- "CLIENT rx ack: " : "server rx ack: ";
+ struct dccp_sock *dp = dccp_sk(sk);
+ const char *debug_prefix =
+ dp->dccps_role = DCCP_ROLE_CLIENT ?
+ "CLIENT rx ack: " : "server rx ack: ";
#endif
- dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, "
- "ack_ackno=%llu, ACKED!\n",
- debug_prefix, 1,
- (unsigned long long)av->dccpav_ack_seqno,
- (unsigned long long)av->dccpav_ack_ackno);
- dccp_ackvec_trow_away_ack_record(av);
- av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
+ dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, "
+ "ack_ackno=%llu, ACKED!\n",
+ debug_prefix, 1,
+ (unsigned long long)avr->dccpavr_ack_seqno,
+ (unsigned long long)avr->dccpavr_ack_ackno);
+ dccp_ackvec_throw_away_ack_record(av, avr);
+ break;
+ }
+ avr = avr->dccpavr_next;
}
}
@@ -337,26 +467,12 @@ static void dccp_ackvec_check_rcv_ackvec
const unsigned char *vector)
{
unsigned char i;
+ struct dccp_ackvec_record *avr;
+ avr = av->dccpav_record;
/* Check if we actually sent an ACK vector */
- if (av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1)
+ if (!avr)
return;
- /*
- * We're in the receiver half connection, so if the received an ACK
- * vector ackno (e.g. 50) before dccpav_ack_seqno (e.g. 52), we're
- * not interested.
- *
- * Extra explanation with example:
- *
- * if we received an ACK vector with ackno 50, it can only be acking
- * 50, 49, 48, etc, not 52 (the seqno for the ACK vector we sent).
- */
- /* dccp_pr_debug("is %llu < %llu? ", ackno, av->dccpav_ack_seqno); */
- if (before48(ackno, av->dccpav_ack_seqno)) {
- /* dccp_pr_debug_cat("yes\n"); */
- return;
- }
- /* dccp_pr_debug_cat("no\n"); */
i = len;
while (i--) {
@@ -365,11 +481,22 @@ static void dccp_ackvec_check_rcv_ackvec
dccp_set_seqno(&ackno_end_rl, ackno - rl);
+ /* if our AVR sequence number is greater than the ack, go
+ * forward in the AVR list until it is not so.
+ */
+ while (avr) {
+ if (!after48(avr->dccpavr_ack_seqno, ackno))
+ break;
+ avr = avr->dccpavr_next;
+ }
+ if (!avr)
+ break;
+
/*
* dccp_pr_debug("is %llu <= %llu <= %llu? ", ackno_end_rl,
* av->dccpav_ack_seqno, ackno);
*/
- if (between48(av->dccpav_ack_seqno, ackno_end_rl, ackno)) {
+ if (between48(avr->dccpavr_ack_seqno, ackno_end_rl, ackno)) {
const u8 state = (*vector &
DCCP_ACKVEC_STATE_MASK) >> 6;
/* dccp_pr_debug_cat("yes\n"); */
@@ -389,14 +516,11 @@ static void dccp_ackvec_check_rcv_ackvec
av->dccpav_ack_seqno,
(unsigned long long)
av->dccpav_ack_ackno);
- dccp_ackvec_trow_away_ack_record(av);
+ dccp_ackvec_throw_away_ack_record(av, avr);
+ break;
}
- /*
- * If dccpav_ack_seqno was not received, no problem
- * we'll send another ACK vector.
- */
- av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
- break;
+ /* If it wasn't received, continue scanning... we might
+ * find another one. */
}
/* dccp_pr_debug_cat("no\n"); */
diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h
index d0fd6c6..4aa16df 100644
--- a/net/dccp/ackvec.h
+++ b/net/dccp/ackvec.h
@@ -26,6 +26,19 @@
#define DCCP_ACKVEC_STATE_MASK 0xC0 /* 11000000 */
#define DCCP_ACKVEC_LEN_MASK 0x3F /* 00111111 */
+/* ACK vector record as defined in Appendix A of spec.
+ * The list is sorted by ack_seqno
+ */
+struct dccp_ackvec_record {
+ u64 dccpavr_ack_seqno;
+ unsigned int dccpavr_ack_ptr;
+ u64 dccpavr_ack_ackno;
+ u8 dccpavr_ack_nonce;
+ unsigned int dccpavr_sent_len;
+ struct dccp_ackvec_record* dccpavr_next;
+ struct dccp_ackvec_record* dccpavr_prev;
+};
+
/** struct dccp_ackvec - ack vector
*
* This data structure is the one defined in the DCCP draft
@@ -54,19 +67,23 @@
* @dccpav_buf - circular buffer of acknowledgeable packets
*/
struct dccp_ackvec {
- unsigned int dccpav_buf_head;
- unsigned int dccpav_buf_tail;
- u64 dccpav_buf_ackno;
- u64 dccpav_ack_seqno;
- u64 dccpav_ack_ackno;
- unsigned int dccpav_ack_ptr;
- unsigned int dccpav_sent_len;
- unsigned int dccpav_vec_len;
- unsigned int dccpav_buf_len;
- struct timeval dccpav_time;
- u8 dccpav_buf_nonce;
- u8 dccpav_ack_nonce;
- u8 dccpav_buf[0];
+ unsigned int dccpav_buf_head;
+ unsigned int dccpav_buf_tail;
+ u64 dccpav_buf_ackno;
+ u64 dccpav_ack_seqno;
+ u64 dccpav_ack_ackno;
+ unsigned int dccpav_ack_ptr;
+ unsigned int dccpav_sent_len;
+ unsigned int dccpav_vec_len;
+ unsigned int dccpav_buf_len;
+ struct timeval dccpav_time;
+ u8 dccpav_buf_nonce;
+ u8 dccpav_ack_nonce;
+ struct dccp_ackvec_record* dccpav_record;
+ struct dccp_ackvec_record** dccpav_record_pool;
+ unsigned int dccpav_record_pool_free;
+ unsigned int dccpav_record_pool_len;
+ u8 dccpav_buf[0];
};
struct sock;
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index 93f26dd..1dcd835 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -226,7 +226,8 @@ extern int dccp_child_process(struct soc
extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
struct dccp_hdr *dh, unsigned len);
extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
- const struct dccp_hdr *dh, const unsigned len);
+ const struct dccp_hdr *dh, const unsigned len,
+ int fastlane);
extern int dccp_v4_init_sock(struct sock *sk);
extern int dccp_v4_destroy_sock(struct sock *sk);
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 55e921b..757539a 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -152,27 +152,36 @@ static int dccp_check_seqno(struct sock
}
int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
- const struct dccp_hdr *dh, const unsigned len)
+ const struct dccp_hdr *dh, const unsigned len,
+ int fastlane)
{
struct dccp_sock *dp = dccp_sk(sk);
- if (dccp_check_seqno(sk, skb))
- goto discard;
+ /* If called from the DCCP_OPEN state [fast path] then perform checks
+ * as upper layers don't. If called from DCCP_PARTOPEN, checks will
+ * be done by upper layers... so don't repeat them. [else ack vector
+ * will be added twice! and packet will be dropped]
+ * -sorbo
+ */
+ if (fastlane) {
+ if (dccp_check_seqno(sk, skb))
+ goto discard;
- if (dccp_parse_options(sk, skb))
- goto discard;
+ if (dccp_parse_options(sk, skb))
+ goto discard;
- if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
- dccp_event_ack_recv(sk, skb);
+ if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
+ dccp_event_ack_recv(sk, skb);
- if (dp->dccps_options.dccpo_send_ack_vector &&
- dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
- DCCP_SKB_CB(skb)->dccpd_seq,
- DCCP_ACKVEC_STATE_RECEIVED))
- goto discard;
+ if (dp->dccps_options.dccpo_send_ack_vector &&
+ dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
+ DCCP_SKB_CB(skb)->dccpd_seq,
+ DCCP_ACKVEC_STATE_RECEIVED))
+ goto discard;
- ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
- ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
+ ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
+ ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
+ }
switch (dccp_hdr(skb)->dccph_type) {
case DCCP_PKT_DATAACK:
@@ -288,6 +297,12 @@ static int dccp_rcv_request_sent_state_p
goto out_invalid_packet;
}
+ if (dp->dccps_options.dccpo_send_ack_vector &&
+ dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
+ DCCP_SKB_CB(skb)->dccpd_seq,
+ DCCP_ACKVEC_STATE_RECEIVED))
+ goto out_invalid_packet; /* FIXME: change error code */
+
dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq;
dccp_update_gsr(sk, dp->dccps_isr);
/*
@@ -400,7 +415,7 @@ static int dccp_rcv_respond_partopen_sta
if (dh->dccph_type = DCCP_PKT_DATAACK ||
dh->dccph_type = DCCP_PKT_DATA) {
- dccp_rcv_established(sk, skb, dh, len);
+ dccp_rcv_established(sk, skb, dh, len, 0);
queued = 1; /* packet was queued
(by dccp_rcv_established) */
}
@@ -474,14 +489,14 @@ int dccp_rcv_state_process(struct sock *
if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
dccp_event_ack_recv(sk, skb);
- ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
- ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
-
if (dp->dccps_options.dccpo_send_ack_vector &&
dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_seq,
DCCP_ACKVEC_STATE_RECEIVED))
goto discard;
+
+ ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
+ ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
}
/*
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index f935659..5fdad99 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -795,7 +795,7 @@ int dccp_v4_do_rcv(struct sock *sk, stru
struct dccp_hdr *dh = dccp_hdr(skb);
if (sk->sk_state = DCCP_OPEN) { /* Fast path */
- if (dccp_rcv_established(sk, skb, dh, skb->len))
+ if (dccp_rcv_established(sk, skb, dh, skb->len, 1))
goto reset;
return 0;
}
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index ba50c96..14f1c13 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -1159,7 +1159,7 @@ static int dccp_v6_do_rcv(struct sock *s
opt_skb = skb_clone(skb, GFP_ATOMIC);
if (sk->sk_state = DCCP_OPEN) { /* Fast path */
- if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len))
+ if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len, 1))
goto reset;
return 0;
}
^ permalink raw reply related
* Re: [PATCH 2/2][IP_SOCKGLUE]: Remove most of the tcp specific calls
From: Arnaldo Carvalho de Melo @ 2005-12-13 3:17 UTC (permalink / raw)
To: dccp
In-Reply-To: <39e6f6c70512121603k4fa2a924k3ee75c1fb6a0fa4e@mail.gmail.com>
On 12/12/05, Arnaldo Carvalho de Melo <acme@ghostprotocols.net> wrote:
> Hi David,
>
> Please consider pulling from:
>
> master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
Oops, there was a bug introduced by this patch at this hunk:
@@ -870,7 +872,7 @@ static struct sock * tcp_v6_syn_recv_soc
worked with IPv6 icsk.icsk_af_ops.
Sync it now.
*/
- tcp_sync_mss(newsk, newtp->pmtu_cookie);
+ tcp_sync_mss(newsk, inet_csk(sk)->icsk_pmtu_cookie);
return newsk;
}
It should have been inet_csk(newsk)-> ...
Fixed in the (reworked) submitted tree.
Best Regards,
- Arnaldo
^ permalink raw reply
* [PATCH 2/2][IP_SOCKGLUE]: Remove most of the tcp specific calls
From: Arnaldo Carvalho de Melo @ 2005-12-13 0:03 UTC (permalink / raw)
To: dccp
[-- Attachment #1: Type: text/plain, Size: 143 bytes --]
Hi David,
Please consider pulling from:
master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
Best Regards,
- Arnaldo
[-- Attachment #2: 2.patch --]
[-- Type: text/x-patch, Size: 27703 bytes --]
tree 99f9e56a48a5a7a783875df630e84c91fd06488a
parent 793a36f474b6d9991f6c1c218041e350cda57195
author Arnaldo Carvalho de Melo <acme@mandriva.com> 1134430852 -0200
committer Arnaldo Carvalho de Melo <acme@mandriva.com> 1134430852 -0200
[IP_SOCKGLUE]: Remove most of the tcp specific calls
As DCCP needs to be called in the same spots.
Now we have a member in inet_sock (is_icsk), set at sock creation time from
struct inet_protosw->flags (if INET_PROTOSW_ICSK is set, like for TCP and
DCCP) to see if a struct sock instance is a inet_connection_sock for places
like the ones in ip_sockglue.c (v4 and v6) where we previously were looking if
sk_type was SOCK_STREAM, that is insufficient because we now use the same code
for DCCP, that has sk_type SOCK_DCCP.
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
------------------------------------------------------------------------------
include/linux/dccp.h | 4 ----
include/linux/ip.h | 1 +
include/linux/tcp.h | 3 +--
include/net/inet_connection_sock.h | 6 +++++-
include/net/protocol.h | 1 +
net/dccp/diag.c | 2 +-
net/dccp/input.c | 2 +-
net/dccp/ipv4.c | 12 +++++++-----
net/dccp/ipv6.c | 26 ++++++++++++++------------
net/dccp/output.c | 7 ++++---
net/dccp/proto.c | 2 +-
net/ipv4/af_inet.c | 4 +++-
net/ipv4/ip_sockglue.c | 13 ++++++-------
net/ipv4/tcp.c | 2 +-
net/ipv4/tcp_input.c | 10 ++++------
net/ipv4/tcp_ipv4.c | 12 ++++++------
net/ipv4/tcp_output.c | 18 ++++++++++--------
net/ipv6/af_inet6.c | 1 +
net/ipv6/ipv6_sockglue.c | 24 +++++++++++++-----------
net/ipv6/tcp_ipv6.c | 30 +++++++++++++++++-------------
20 files changed, 97 insertions(+), 83 deletions(-)
------------------------------------------------------------------------------
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 71fab43..d0bdb49 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -408,8 +408,6 @@ struct dccp_ackvec;
* @dccps_gar - greatest valid ack number received on a non-Sync; initialized to %dccps_iss
* @dccps_timestamp_time - time of latest TIMESTAMP option
* @dccps_timestamp_echo - latest timestamp received on a TIMESTAMP option
- * @dccps_ext_header_len - network protocol overhead (IP/IPv6 options)
- * @dccps_pmtu_cookie - Last pmtu seen by socket
* @dccps_packet_size - Set thru setsockopt
* @dccps_role - Role of this sock, one of %dccp_role
* @dccps_ndp_count - number of Non Data Packets since last data packet
@@ -434,8 +432,6 @@ struct dccp_sock {
__u32 dccps_timestamp_echo;
__u32 dccps_packet_size;
unsigned long dccps_ndp_count;
- __u16 dccps_ext_header_len;
- __u32 dccps_pmtu_cookie;
__u32 dccps_mss_cache;
struct dccp_options dccps_options;
struct dccp_ackvec *dccps_hc_rx_ackvec;
diff --git a/include/linux/ip.h b/include/linux/ip.h
index 5a560da..6ccc596 100644
--- a/include/linux/ip.h
+++ b/include/linux/ip.h
@@ -155,6 +155,7 @@ struct inet_sock {
__u8 mc_ttl; /* Multicasting TTL */
__u8 pmtudisc;
unsigned recverr : 1,
+ is_icsk : 1, /* inet_connection_sock? */
freebind : 1,
hdrincl : 1,
mc_loop : 1;
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index da38eea..f2bb239 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -238,10 +238,9 @@ struct tcp_sock {
__u32 snd_wl1; /* Sequence for window update */
__u32 snd_wnd; /* The window we expect to receive */
__u32 max_window; /* Maximal window ever seen from peer */
- __u32 pmtu_cookie; /* Last pmtu seen by socket */
__u32 mss_cache; /* Cached effective mss, not including SACKS */
__u16 xmit_size_goal; /* Goal for segmenting output packets */
- __u16 ext_header_len; /* Network protocol overhead (IP/IPv6 options) */
+ /* XXX Two bytes hole, try to pack */
__u32 window_clamp; /* Maximal window to advertise */
__u32 rcv_ssthresh; /* Current window clamp */
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index e50e2b8..9188896 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -60,6 +60,7 @@ struct inet_connection_sock_af_ops {
* @icsk_timeout: Timeout
* @icsk_retransmit_timer: Resend (no ack)
* @icsk_rto: Retransmit timeout
+ * @icsk_pmtu_cookie Last pmtu seen by socket
* @icsk_ca_ops Pluggable congestion control hook
* @icsk_af_ops Operations which are AF_INET{4,6} specific
* @icsk_ca_state: Congestion control state
@@ -68,6 +69,7 @@ struct inet_connection_sock_af_ops {
* @icsk_backoff: Backoff
* @icsk_syn_retries: Number of allowed SYN (or equivalent) retries
* @icsk_probes_out: unanswered 0 window probes
+ * @icsk_ext_hdr_len: Network protocol overhead (IP/IPv6 options)
* @icsk_ack: Delayed ACK control data
*/
struct inet_connection_sock {
@@ -79,15 +81,17 @@ struct inet_connection_sock {
struct timer_list icsk_retransmit_timer;
struct timer_list icsk_delack_timer;
__u32 icsk_rto;
+ __u32 icsk_pmtu_cookie;
struct tcp_congestion_ops *icsk_ca_ops;
struct inet_connection_sock_af_ops *icsk_af_ops;
+ unsigned int (*icsk_sync_mss)(struct sock *sk, u32 pmtu);
__u8 icsk_ca_state;
__u8 icsk_retransmits;
__u8 icsk_pending;
__u8 icsk_backoff;
__u8 icsk_syn_retries;
__u8 icsk_probes_out;
- /* 2 BYTES HOLE, TRY TO PACK! */
+ __u16 icsk_ext_hdr_len;
struct {
__u8 pending; /* ACK is pending */
__u8 quick; /* Scheduled number of quick acks */
diff --git a/include/net/protocol.h b/include/net/protocol.h
index 357691f..a29cb29 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -76,6 +76,7 @@ struct inet_protosw {
};
#define INET_PROTOSW_REUSE 0x01 /* Are ports automatically reusable? */
#define INET_PROTOSW_PERMANENT 0x02 /* Permanent protocols are unremovable. */
+#define INET_PROTOSW_ICSK 0x04 /* Is this an inet_connection_sock? */
extern struct net_protocol *inet_protocol_base;
extern struct net_protocol *inet_protos[MAX_INET_PROTOS];
diff --git a/net/dccp/diag.c b/net/dccp/diag.c
index f675d8e..3f78c00 100644
--- a/net/dccp/diag.c
+++ b/net/dccp/diag.c
@@ -28,7 +28,7 @@ static void dccp_get_info(struct sock *s
info->tcpi_retransmits = icsk->icsk_retransmits;
info->tcpi_probes = icsk->icsk_probes_out;
info->tcpi_backoff = icsk->icsk_backoff;
- info->tcpi_pmtu = dp->dccps_pmtu_cookie;
+ info->tcpi_pmtu = icsk->icsk_pmtu_cookie;
if (dp->dccps_options.dccpo_send_ack_vector)
info->tcpi_options |= TCPI_OPT_SACK;
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 9a724ff..55e921b 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -311,7 +311,7 @@ static int dccp_rcv_request_sent_state_p
goto out_invalid_packet;
}
- dccp_sync_mss(sk, dp->dccps_pmtu_cookie);
+ dccp_sync_mss(sk, icsk->icsk_pmtu_cookie);
/*
* Step 10: Process REQUEST state (second part)
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 2702094..f935659 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -104,9 +104,9 @@ int dccp_v4_connect(struct sock *sk, str
inet->dport = usin->sin_port;
inet->daddr = daddr;
- dp->dccps_ext_header_len = 0;
+ inet_csk(sk)->icsk_ext_hdr_len = 0;
if (inet->opt != NULL)
- dp->dccps_ext_header_len = inet->opt->optlen;
+ inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
/*
* Socket identity is still unknown (sport may be zero).
* However we set state to DCCP_REQUESTING and not releasing socket
@@ -191,7 +191,7 @@ static inline void dccp_do_pmtu_discover
mtu = dst_mtu(dst);
if (inet->pmtudisc != IP_PMTUDISC_DONT &&
- dp->dccps_pmtu_cookie > mtu) {
+ inet_csk(sk)->icsk_pmtu_cookie > mtu) {
dccp_sync_mss(sk, mtu);
/*
@@ -1051,6 +1051,7 @@ struct inet_connection_sock_af_ops dccp_
int dccp_v4_init_sock(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
static int dccp_ctl_socket_init = 1;
dccp_options_init(&dp->dccps_options);
@@ -1090,10 +1091,11 @@ int dccp_v4_init_sock(struct sock *sk)
dccp_ctl_socket_init = 0;
dccp_init_xmit_timers(sk);
- inet_csk(sk)->icsk_rto = DCCP_TIMEOUT_INIT;
+ icsk->icsk_rto = DCCP_TIMEOUT_INIT;
sk->sk_state = DCCP_CLOSED;
sk->sk_write_space = dccp_write_space;
- inet_csk(sk)->icsk_af_ops = &dccp_ipv4_af_ops;
+ icsk->icsk_af_ops = &dccp_ipv4_af_ops;
+ icsk->icsk_sync_mss = dccp_sync_mss;
dp->dccps_mss_cache = 536;
dp->dccps_role = DCCP_ROLE_UNDEFINED;
dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 71bf04e..599b0be 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -88,6 +88,7 @@ static int dccp_v6_connect(struct sock *
int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct dccp_sock *dp = dccp_sk(sk);
@@ -158,7 +159,7 @@ static int dccp_v6_connect(struct sock *
*/
if (addr_type == IPV6_ADDR_MAPPED) {
- u32 exthdrlen = dp->dccps_ext_header_len;
+ u32 exthdrlen = icsk->icsk_ext_hdr_len;
struct sockaddr_in sin;
SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
@@ -170,14 +171,14 @@ static int dccp_v6_connect(struct sock *
sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
- inet_csk(sk)->icsk_af_ops = &dccp_ipv6_mapped;
+ icsk->icsk_af_ops = &dccp_ipv6_mapped;
sk->sk_backlog_rcv = dccp_v4_do_rcv;
err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
if (err) {
- dp->dccps_ext_header_len = exthdrlen;
- inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops;
+ icsk->icsk_ext_hdr_len = exthdrlen;
+ icsk->icsk_af_ops = &dccp_ipv6_af_ops;
sk->sk_backlog_rcv = dccp_v6_do_rcv;
goto failure;
} else {
@@ -227,9 +228,10 @@ static int dccp_v6_connect(struct sock *
ip6_dst_store(sk, dst, NULL);
- dp->dccps_ext_header_len = 0;
+ icsk->icsk_ext_hdr_len = 0;
if (np->opt)
- dp->dccps_ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+ icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
+ np->opt->opt_nflen);
inet->dport = usin->sin6_port;
@@ -292,7 +294,6 @@ static void dccp_v6_err(struct sk_buff *
np = inet6_sk(sk);
if (type == ICMPV6_PKT_TOOBIG) {
- struct dccp_sock *dp = dccp_sk(sk);
struct dst_entry *dst = NULL;
if (sock_owned_by_user(sk))
@@ -332,7 +333,7 @@ static void dccp_v6_err(struct sk_buff *
} else
dst_hold(dst);
- if (dp->dccps_pmtu_cookie > dst_mtu(dst)) {
+ if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
dccp_sync_mss(sk, dst_mtu(dst));
} /* else let the usual retransmit timer handle it */
dst_release(dst);
@@ -808,7 +809,7 @@ static struct sock *dccp_v6_request_recv
worked with IPv6 icsk.icsk_af_ops.
Sync it now.
*/
- dccp_sync_mss(newsk, newdp->dccps_pmtu_cookie);
+ dccp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
return newsk;
}
@@ -916,10 +917,10 @@ static struct sock *dccp_v6_request_recv
sock_kfree_s(sk, opt, opt->tot_len);
}
- newdp->dccps_ext_header_len = 0;
+ inet_csk(newsk)->icsk_ext_hdr_len = 0;
if (newnp->opt)
- newdp->dccps_ext_header_len = newnp->opt->opt_nflen +
- newnp->opt->opt_flen;
+ inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
+ newnp->opt->opt_flen);
dccp_sync_mss(newsk, dst_mtu(dst));
@@ -1230,6 +1231,7 @@ static struct inet_protosw dccp_v6_proto
.prot = &dccp_v6_prot,
.ops = &inet6_dccp_ops,
.capability = -1,
+ .flags = INET_PROTOSW_ICSK,
};
static int __init dccp_v6_init(void)
diff --git a/net/dccp/output.c b/net/dccp/output.c
index c40f7f8..95a3c2c 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -134,12 +134,13 @@ static int dccp_transmit_skb(struct sock
unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu)
{
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct dccp_sock *dp = dccp_sk(sk);
- int mss_now = (pmtu - inet_csk(sk)->icsk_af_ops->net_header_len -
+ int mss_now = (pmtu - icsk->icsk_af_ops->net_header_len -
sizeof(struct dccp_hdr) - sizeof(struct dccp_hdr_ext));
/* Now subtract optional transport overhead */
- mss_now -= dp->dccps_ext_header_len;
+ mss_now -= icsk->icsk_ext_hdr_len;
/*
* FIXME: this should come from the CCID infrastructure, where, say,
@@ -152,7 +153,7 @@ unsigned int dccp_sync_mss(struct sock *
mss_now -= ((5 + 6 + 10 + 6 + 6 + 6 + 3) / 4) * 4;
/* And store cached results */
- dp->dccps_pmtu_cookie = pmtu;
+ icsk->icsk_pmtu_cookie = pmtu;
dp->dccps_mss_cache = mss_now;
return mss_now;
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 51dfacd..40a4c68 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -712,7 +712,7 @@ static struct inet_protosw dccp_v4_proto
.ops = &inet_dccp_ops,
.capability = -1,
.no_check = 0,
- .flags = 0,
+ .flags = INET_PROTOSW_ICSK,
};
/*
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index d368cf2..617e858 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -302,6 +302,7 @@ lookup_protocol:
sk->sk_reuse = 1;
inet = inet_sk(sk);
+ inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;
if (SOCK_RAW == sock->type) {
inet->num = protocol;
@@ -869,7 +870,8 @@ static struct inet_protosw inetsw_array[
.ops = &inet_stream_ops,
.capability = -1,
.no_check = 0,
- .flags = INET_PROTOSW_PERMANENT,
+ .flags = INET_PROTOSW_PERMANENT |
+ INET_PROTOSW_ICSK,
},
{
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 4f2d872..add019c 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -29,8 +29,7 @@
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
-#include <net/tcp.h>
-#include <linux/tcp.h>
+#include <net/tcp_states.h>
#include <linux/udp.h>
#include <linux/igmp.h>
#include <linux/netfilter.h>
@@ -427,8 +426,8 @@ int ip_setsockopt(struct sock *sk, int l
err = ip_options_get_from_user(&opt, optval, optlen);
if (err)
break;
- if (sk->sk_type == SOCK_STREAM) {
- struct tcp_sock *tp = tcp_sk(sk);
+ if (inet->is_icsk) {
+ struct inet_connection_sock *icsk = inet_csk(sk);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
if (sk->sk_family == PF_INET ||
(!((1 << sk->sk_state) &
@@ -436,10 +435,10 @@ int ip_setsockopt(struct sock *sk, int l
inet->daddr != LOOPBACK4_IPV6)) {
#endif
if (inet->opt)
- tp->ext_header_len -= inet->opt->optlen;
+ icsk->icsk_ext_hdr_len -= inet->opt->optlen;
if (opt)
- tp->ext_header_len += opt->optlen;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ icsk->icsk_ext_hdr_len += opt->optlen;
+ icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
}
#endif
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index eacfe6a..00aa80e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1914,7 +1914,7 @@ void tcp_get_info(struct sock *sk, struc
info->tcpi_last_data_recv = jiffies_to_msecs(now - icsk->icsk_ack.lrcvtime);
info->tcpi_last_ack_recv = jiffies_to_msecs(now - tp->rcv_tstamp);
- info->tcpi_pmtu = tp->pmtu_cookie;
+ info->tcpi_pmtu = icsk->icsk_pmtu_cookie;
info->tcpi_rcv_ssthresh = tp->rcv_ssthresh;
info->tcpi_rtt = jiffies_to_usecs(tp->srtt)>>3;
info->tcpi_rttvar = jiffies_to_usecs(tp->mdev)>>2;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 7de6184..981d120 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2342,7 +2342,7 @@ static int tcp_ack_update_window(struct
if (nwin > tp->max_window) {
tp->max_window = nwin;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, inet_csk(sk)->icsk_pmtu_cookie);
}
}
}
@@ -3967,12 +3967,12 @@ static int tcp_rcv_synsent_state_process
struct tcphdr *th, unsigned len)
{
struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
int saved_clamp = tp->rx_opt.mss_clamp;
tcp_parse_options(skb, &tp->rx_opt, 0);
if (th->ack) {
- struct inet_connection_sock *icsk;
/* rfc793:
* "If the state is SYN-SENT then
* first check the ACK bit
@@ -4061,7 +4061,7 @@ static int tcp_rcv_synsent_state_process
if (tp->rx_opt.sack_ok && sysctl_tcp_fack)
tp->rx_opt.sack_ok |= 2;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
tcp_initialize_rcv_mss(sk);
/* Remember, tcp_poll() does not lock socket!
@@ -4071,8 +4071,6 @@ static int tcp_rcv_synsent_state_process
mb();
tcp_set_state(sk, TCP_ESTABLISHED);
- icsk = inet_csk(sk);
-
/* Make sure socket is routed, for correct metrics. */
icsk->icsk_af_ops->rebuild_header(sk);
@@ -4173,7 +4171,7 @@ discard:
if (tp->ecn_flags&TCP_ECN_OK)
sock_set_flag(sk, SOCK_NO_LARGESEND);
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
tcp_initialize_rcv_mss(sk);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index c2fe61b..9b62d80 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -220,9 +220,9 @@ int tcp_v4_connect(struct sock *sk, stru
inet->dport = usin->sin_port;
inet->daddr = daddr;
- tp->ext_header_len = 0;
+ inet_csk(sk)->icsk_ext_hdr_len = 0;
if (inet->opt)
- tp->ext_header_len = inet->opt->optlen;
+ inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
tp->rx_opt.mss_clamp = 536;
@@ -275,7 +275,6 @@ static inline void do_pmtu_discovery(str
{
struct dst_entry *dst;
struct inet_sock *inet = inet_sk(sk);
- struct tcp_sock *tp = tcp_sk(sk);
/* We are not interested in TCP_LISTEN and open_requests (SYN-ACKs
* send out by Linux are always <576bytes so they should go through
@@ -304,7 +303,7 @@ static inline void do_pmtu_discovery(str
mtu = dst_mtu(dst);
if (inet->pmtudisc != IP_PMTUDISC_DONT &&
- tp->pmtu_cookie > mtu) {
+ inet_csk(sk)->icsk_pmtu_cookie > mtu) {
tcp_sync_mss(sk, mtu);
/* Resend the TCP packet because it's
@@ -895,9 +894,9 @@ struct sock *tcp_v4_syn_recv_sock(struct
ireq->opt = NULL;
newinet->mc_index = inet_iif(skb);
newinet->mc_ttl = skb->nh.iph->ttl;
- newtp->ext_header_len = 0;
+ inet_csk(newsk)->icsk_ext_hdr_len = 0;
if (newinet->opt)
- newtp->ext_header_len = newinet->opt->optlen;
+ inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen;
newinet->id = newtp->write_seq ^ jiffies;
tcp_sync_mss(newsk, dst_mtu(dst));
@@ -1266,6 +1265,7 @@ static int tcp_v4_init_sock(struct sock
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
icsk->icsk_af_ops = &ipv4_specific;
+ icsk->icsk_sync_mss = tcp_sync_mss;
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index af1946c..3a0a914 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -621,7 +621,7 @@ int tcp_trim_head(struct sock *sk, struc
It is minimum of user_mss and mss received with SYN.
It also does not include TCP options.
- tp->pmtu_cookie is last pmtu, seen by this function.
+ inet_csk(sk)->icsk_pmtu_cookie is last pmtu, seen by this function.
tp->mss_cache is current effective sending mss, including
all tcp options except for SACKs. It is evaluated,
@@ -631,17 +631,18 @@ int tcp_trim_head(struct sock *sk, struc
NOTE1. rfc1122 clearly states that advertised MSS
DOES NOT include either tcp or ip options.
- NOTE2. tp->pmtu_cookie and tp->mss_cache are READ ONLY outside
- this function. --ANK (980731)
+ NOTE2. inet_csk(sk)->icsk_pmtu_cookie and tp->mss_cache
+ are READ ONLY outside this function. --ANK (980731)
*/
unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu)
{
struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
/* Calculate base mss without TCP options:
It is MMS_S - sizeof(tcphdr) of rfc1122
*/
- int mss_now = (pmtu - inet_csk(sk)->icsk_af_ops->net_header_len -
+ int mss_now = (pmtu - icsk->icsk_af_ops->net_header_len -
sizeof(struct tcphdr));
/* Clamp it (mss_clamp does not include tcp options) */
@@ -649,7 +650,7 @@ unsigned int tcp_sync_mss(struct sock *s
mss_now = tp->rx_opt.mss_clamp;
/* Now subtract optional transport overhead */
- mss_now -= tp->ext_header_len;
+ mss_now -= icsk->icsk_ext_hdr_len;
/* Then reserve room for full set of TCP options and 8 bytes of data */
if (mss_now < 48)
@@ -663,7 +664,7 @@ unsigned int tcp_sync_mss(struct sock *s
mss_now = max((tp->max_window>>1), 68U - tp->tcp_header_len);
/* And store cached results */
- tp->pmtu_cookie = pmtu;
+ icsk->icsk_pmtu_cookie = pmtu;
tp->mss_cache = mss_now;
return mss_now;
@@ -693,7 +694,7 @@ unsigned int tcp_current_mss(struct sock
if (dst) {
u32 mtu = dst_mtu(dst);
- if (mtu != tp->pmtu_cookie)
+ if (mtu != inet_csk(sk)->icsk_pmtu_cookie)
mss_now = tcp_sync_mss(sk, mtu);
}
@@ -706,7 +707,8 @@ unsigned int tcp_current_mss(struct sock
if (doing_tso) {
xmit_size_goal = (65535 -
inet_csk(sk)->icsk_af_ops->net_header_len -
- tp->ext_header_len - tp->tcp_header_len);
+ inet_csk(sk)->icsk_ext_hdr_len -
+ tp->tcp_header_len);
if (tp->max_window &&
(xmit_size_goal > (tp->max_window >> 1)))
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index bf17aab..70a510f 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -167,6 +167,7 @@ lookup_protocol:
sk->sk_reuse = 1;
inet = inet_sk(sk);
+ inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;
if (SOCK_RAW == sock->type) {
inet->num = protocol;
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index b6b63fa..c63868d 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -163,17 +163,17 @@ int ipv6_setsockopt(struct sock *sk, int
sk_refcnt_debug_dec(sk);
if (sk->sk_protocol == IPPROTO_TCP) {
- struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
local_bh_disable();
sock_prot_dec_use(sk->sk_prot);
sock_prot_inc_use(&tcp_prot);
local_bh_enable();
sk->sk_prot = &tcp_prot;
- inet_csk(sk)->icsk_af_ops = &ipv4_specific;
+ icsk->icsk_af_ops = &ipv4_specific;
sk->sk_socket->ops = &inet_stream_ops;
sk->sk_family = PF_INET;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
} else {
local_bh_disable();
sock_prot_dec_use(sk->sk_prot);
@@ -317,14 +317,15 @@ int ipv6_setsockopt(struct sock *sk, int
}
retv = 0;
- if (sk->sk_type == SOCK_STREAM) {
+ if (inet_sk(sk)->is_icsk) {
if (opt) {
- struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
if (!((1 << sk->sk_state) &
(TCPF_LISTEN | TCPF_CLOSE))
&& inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
- tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ icsk->icsk_ext_hdr_len =
+ opt->opt_flen + opt->opt_nflen;
+ icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
}
}
opt = xchg(&np->opt, opt);
@@ -380,14 +381,15 @@ sticky_done:
goto done;
update:
retv = 0;
- if (sk->sk_type == SOCK_STREAM) {
+ if (inet_sk(sk)->is_icsk) {
if (opt) {
- struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
if (!((1 << sk->sk_state) &
(TCPF_LISTEN | TCPF_CLOSE))
&& inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
- tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ icsk->icsk_ext_hdr_len =
+ opt->opt_flen + opt->opt_nflen;
+ icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
}
}
opt = xchg(&np->opt, opt);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 1587c35..1cc6f4e 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -123,7 +123,8 @@ static int tcp_v6_connect(struct sock *s
int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
- struct inet_sock *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct tcp_sock *tp = tcp_sk(sk);
struct in6_addr *saddr = NULL, *final_p = NULL, final;
@@ -198,7 +199,7 @@ static int tcp_v6_connect(struct sock *s
*/
if (addr_type == IPV6_ADDR_MAPPED) {
- u32 exthdrlen = tp->ext_header_len;
+ u32 exthdrlen = icsk->icsk_ext_hdr_len;
struct sockaddr_in sin;
SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
@@ -210,14 +211,14 @@ static int tcp_v6_connect(struct sock *s
sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
- inet_csk(sk)->icsk_af_ops = &ipv6_mapped;
+ icsk->icsk_af_ops = &ipv6_mapped;
sk->sk_backlog_rcv = tcp_v4_do_rcv;
err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
if (err) {
- tp->ext_header_len = exthdrlen;
- inet_csk(sk)->icsk_af_ops = &ipv6_specific;
+ icsk->icsk_ext_hdr_len = exthdrlen;
+ icsk->icsk_af_ops = &ipv6_specific;
sk->sk_backlog_rcv = tcp_v6_do_rcv;
goto failure;
} else {
@@ -270,9 +271,10 @@ static int tcp_v6_connect(struct sock *s
sk->sk_route_caps = dst->dev->features &
~(NETIF_F_IP_CSUM | NETIF_F_TSO);
- tp->ext_header_len = 0;
+ icsk->icsk_ext_hdr_len = 0;
if (np->opt)
- tp->ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+ icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
+ np->opt->opt_nflen);
tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
@@ -385,7 +387,7 @@ static void tcp_v6_err(struct sk_buff *s
} else
dst_hold(dst);
- if (tp->pmtu_cookie > dst_mtu(dst)) {
+ if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
tcp_sync_mss(sk, dst_mtu(dst));
tcp_simple_retransmit(sk);
} /* else let the usual retransmit timer handle it */
@@ -870,7 +872,7 @@ static struct sock * tcp_v6_syn_recv_soc
worked with IPv6 icsk.icsk_af_ops.
Sync it now.
*/
- tcp_sync_mss(newsk, newtp->pmtu_cookie);
+ tcp_sync_mss(newsk, inet_csk(sk)->icsk_pmtu_cookie);
return newsk;
}
@@ -977,10 +979,10 @@ static struct sock * tcp_v6_syn_recv_soc
sock_kfree_s(sk, opt, opt->tot_len);
}
- newtp->ext_header_len = 0;
+ inet_csk(newsk)->icsk_ext_hdr_len = 0;
if (newnp->opt)
- newtp->ext_header_len = newnp->opt->opt_nflen +
- newnp->opt->opt_flen;
+ inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
+ newnp->opt->opt_flen);
tcp_sync_mss(newsk, dst_mtu(dst));
newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
@@ -1362,6 +1364,7 @@ static int tcp_v6_init_sock(struct sock
icsk->icsk_af_ops = &ipv6_specific;
icsk->icsk_ca_ops = &tcp_init_congestion_ops;
+ icsk->icsk_sync_mss = tcp_sync_mss;
sk->sk_write_space = sk_stream_write_space;
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
@@ -1592,7 +1595,8 @@ static struct inet_protosw tcpv6_protosw
.ops = &inet6_stream_ops,
.capability = -1,
.no_check = 0,
- .flags = INET_PROTOSW_PERMANENT,
+ .flags = INET_PROTOSW_PERMANENT |
+ INET_PROTOSW_ICSK,
};
void __init tcpv6_init(void)
^ permalink raw reply related
* [PATCH 1/2][TCP]: Move the TCPF_ enum to tcp_states.h
From: Arnaldo Carvalho de Melo @ 2005-12-13 0:03 UTC (permalink / raw)
To: dccp
[-- Attachment #1: Type: text/plain, Size: 252 bytes --]
Hi David,
Please consider pulling from:
master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
With these two my old DCCPv6 tree finally gets completely submitted,
whee! :-) Now to do new work :-)
Best Regards,
- Arnaldo
[-- Attachment #2: 1.patch --]
[-- Type: text/x-patch, Size: 2056 bytes --]
tree 43548c6bc8d43ef3dbf077d32ee4b495c46b4a78
parent 0c54986a2b9abe6295ccac354924f563fd5dfc9c
author Arnaldo Carvalho de Melo <acme@mandriva.com> 1134427004 -0200
committer Arnaldo Carvalho de Melo <acme@mandriva.com> 1134427004 -0200
[TCP]: Move the TCPF_ enum to tcp_states.h
Upcoming patches will make, for instance, ip_sockglue.c need just this enum
and not all of tcp.h.
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
------------------------------------------------------------------------------
linux/tcp.h | 16 ----------------
net/tcp_states.h | 16 ++++++++++++++++
2 files changed, 16 insertions(+), 16 deletions(-)
------------------------------------------------------------------------------
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 4e14340..da38eea 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -55,22 +55,6 @@ struct tcphdr {
__u16 urg_ptr;
};
-#define TCP_ACTION_FIN (1 << 7)
-
-enum {
- TCPF_ESTABLISHED = (1 << 1),
- TCPF_SYN_SENT = (1 << 2),
- TCPF_SYN_RECV = (1 << 3),
- TCPF_FIN_WAIT1 = (1 << 4),
- TCPF_FIN_WAIT2 = (1 << 5),
- TCPF_TIME_WAIT = (1 << 6),
- TCPF_CLOSE = (1 << 7),
- TCPF_CLOSE_WAIT = (1 << 8),
- TCPF_LAST_ACK = (1 << 9),
- TCPF_LISTEN = (1 << 10),
- TCPF_CLOSING = (1 << 11)
-};
-
/*
* The union cast uses a gcc extension to avoid aliasing problems
* (union is compatible to any of its members)
diff --git a/include/net/tcp_states.h b/include/net/tcp_states.h
index b9d4176..b0b6459 100644
--- a/include/net/tcp_states.h
+++ b/include/net/tcp_states.h
@@ -31,4 +31,20 @@ enum {
#define TCP_STATE_MASK 0xF
+#define TCP_ACTION_FIN (1 << 7)
+
+enum {
+ TCPF_ESTABLISHED = (1 << 1),
+ TCPF_SYN_SENT = (1 << 2),
+ TCPF_SYN_RECV = (1 << 3),
+ TCPF_FIN_WAIT1 = (1 << 4),
+ TCPF_FIN_WAIT2 = (1 << 5),
+ TCPF_TIME_WAIT = (1 << 6),
+ TCPF_CLOSE = (1 << 7),
+ TCPF_CLOSE_WAIT = (1 << 8),
+ TCPF_LAST_ACK = (1 << 9),
+ TCPF_LISTEN = (1 << 10),
+ TCPF_CLOSING = (1 << 11)
+};
+
#endif /* _LINUX_TCP_STATES_H */
^ permalink raw reply related
* Re: [PATCH 1/1][INET6]: Generalise tcp_v6_hash_connect
From: David S. Miller @ 2005-12-12 22:45 UTC (permalink / raw)
To: dccp
In-Reply-To: <39e6f6c70512120812r5db1dcd8y92a176f0d938aecc@mail.gmail.com>
Patch applied, thanks Arnaldo.
^ permalink raw reply
* [PATCH 1/1][INET6]: Generalise tcp_v6_hash_connect
From: Arnaldo Carvalho de Melo @ 2005-12-12 16:12 UTC (permalink / raw)
To: dccp
[-- Attachment #1: Type: text/plain, Size: 845 bytes --]
Hi David,
Please consider pulling from:
master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
Later today I'll continue, chunking out the last changeset in my old
dccpv6 tree, removing the TCP specific bits in ip_sockglue, making it
deal with inet_connection_socks and not anymore with tcp_socks, i.e.
not checking SOCK_STREAM, but a bit in inet_sock that will tell if the
sock is a inet_connection_sock (DCCP (SOCK_DCCP), TCP (SOCK_STREAM),
etc), moving sync_mss to inet_connection_sock::icsk_sync_mss, etc.
Best Regards,
- Arnaldo
P.S.: /me itching to dive into SCTP to see how much code can be
ditched using this brave "new" world of inet_connection_socks, etc :-)
P.S/2: I'd be even more delighted if some SCTP guy could assess the
potential for code sharing with TCP and DCCP using this stuff 8-)
[-- Attachment #2: 1.patch --]
[-- Type: text/x-patch, Size: 18768 bytes --]
tree ee42f43bbd3664a2330b80c274114db19152e9a2
parent f85287378e45fe8dcbbab84e79d263133205f63c
author Arnaldo Carvalho de Melo <acme@mandriva.com> 1134402446 -0200
committer Arnaldo Carvalho de Melo <acme@mandriva.com> 1134402446 -0200
[INET6]: Generalise tcp_v6_hash_connect
Renaming it to inet6_hash_connect, making it possible to ditch
dccp_v6_hash_connect and share the same code with TCP instead.
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
------------------------------------------------------------------------------
drivers/char/random.c | 4
include/linux/random.h | 4
include/net/ipv6.h | 3
net/dccp/ipv6.c | 171 -----------------------------------------
net/ipv6/inet6_hashtables.c | 183 +++++++++++++++++++++++++++++++++++++++++++-
net/ipv6/tcp_ipv6.c | 173 -----------------------------------------
6 files changed, 190 insertions(+), 348 deletions(-)
------------------------------------------------------------------------------
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 79b59d9..bdfdfd2 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1573,7 +1573,7 @@ u32 secure_ipv4_port_ephemeral(__u32 sad
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dport)
+u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dport)
{
struct keydata *keyptr = get_keyptr();
u32 hash[12];
@@ -1584,7 +1584,7 @@ u32 secure_tcpv6_port_ephemeral(const __
return twothirdsMD4Transform(daddr, hash);
}
-EXPORT_SYMBOL(secure_tcpv6_port_ephemeral);
+EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
#endif
#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
diff --git a/include/linux/random.h b/include/linux/random.h
index 01424a8..5d6456b 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -53,8 +53,8 @@ void generate_random_uuid(unsigned char
extern __u32 secure_ip_id(__u32 daddr);
extern u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport);
-extern u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr,
- __u16 dport);
+extern u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr,
+ __u16 dport);
extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
__u16 sport, __u16 dport);
extern __u32 secure_tcpv6_sequence_number(__u32 *saddr, __u32 *daddr,
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 8513761..e3d5d7b 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -527,6 +527,9 @@ extern int inet6_getname(struct socket *
extern int inet6_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg);
+extern int inet6_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk);
+
/*
* reassembly.c
*/
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 4d078f5..71bf04e 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -84,175 +84,6 @@ static __u32 dccp_v6_init_sequence(struc
dh->dccph_sport);
}
-static int __dccp_v6_check_established(struct sock *sk, const __u16 lport,
- struct inet_timewait_sock **twp)
-{
- struct inet_sock *inet = inet_sk(sk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
- const struct in6_addr *daddr = &np->rcv_saddr;
- const struct in6_addr *saddr = &np->daddr;
- const int dif = sk->sk_bound_dev_if;
- const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
- const unsigned int hash = inet6_ehashfn(daddr, inet->num,
- saddr, inet->dport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash);
- struct sock *sk2;
- const struct hlist_node *node;
- struct inet_timewait_sock *tw;
-
- prefetch(head->chain.first);
- write_lock(&head->lock);
-
- /* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) {
- const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
-
- tw = inet_twsk(sk2);
-
- if(*((__u32 *)&(tw->tw_dport)) == ports &&
- sk2->sk_family == PF_INET6 &&
- ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
- ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
- sk2->sk_bound_dev_if == sk->sk_bound_dev_if)
- goto not_unique;
- }
- tw = NULL;
-
- /* And established part... */
- sk_for_each(sk2, node, &head->chain) {
- if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
- goto not_unique;
- }
-
- BUG_TRAP(sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
- sk->sk_hash = hash;
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(&head->lock);
-
- if (twp) {
- *twp = tw;
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
- } else if (tw) {
- /* Silly. Should hash-dance instead... */
- inet_twsk_deschedule(tw, &dccp_death_row);
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
- inet_twsk_put(tw);
- }
- return 0;
-
-not_unique:
- write_unlock(&head->lock);
- return -EADDRNOTAVAIL;
-}
-
-static inline u32 dccp_v6_port_offset(const struct sock *sk)
-{
- const struct inet_sock *inet = inet_sk(sk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
-
- return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
- np->daddr.s6_addr32,
- inet->dport);
-}
-
-static int dccp_v6_hash_connect(struct sock *sk)
-{
- const unsigned short snum = inet_sk(sk)->num;
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- int ret;
-
- if (snum == 0) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int range = high - low;
- int i;
- int port;
- static u32 hint;
- u32 offset = hint + dccp_v6_port_offset(sk);
- struct hlist_node *node;
- struct inet_timewait_sock *tw = NULL;
-
- local_bh_disable();
- for (i = 1; i <= range; i++) {
- port = low + (i + offset) % range;
- head = &dccp_hashinfo.bhash[inet_bhashfn(port,
- dccp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
-
- /* Does not bother with rcv_saddr checks,
- * because the established check is already
- * unique enough.
- */
- inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->port == port) {
- BUG_TRAP(!hlist_empty(&tb->owners));
- if (tb->fastreuse >= 0)
- goto next_port;
- if (!__dccp_v6_check_established(sk,
- port,
- &tw))
- goto ok;
- goto next_port;
- }
- }
-
- tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep,
- head, port);
- if (!tb) {
- spin_unlock(&head->lock);
- break;
- }
- tb->fastreuse = -1;
- goto ok;
-
- next_port:
- spin_unlock(&head->lock);
- }
- local_bh_enable();
-
- return -EADDRNOTAVAIL;
-ok:
- hint += i;
-
- /* Head lock still held and bh's disabled */
- inet_bind_hash(sk, tb, port);
- if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(port);
- __inet6_hash(&dccp_hashinfo, sk);
- }
- spin_unlock(&head->lock);
-
- if (tw) {
- inet_twsk_deschedule(tw, &dccp_death_row);
- inet_twsk_put(tw);
- }
-
- ret = 0;
- goto out;
- }
-
- head = &dccp_hashinfo.bhash[inet_bhashfn(snum,
- dccp_hashinfo.bhash_size)];
- tb = inet_csk(sk)->icsk_bind_hash;
- spin_lock_bh(&head->lock);
-
- if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
- __inet6_hash(&dccp_hashinfo, sk);
- spin_unlock_bh(&head->lock);
- return 0;
- } else {
- spin_unlock(&head->lock);
- /* No definite answer... Walk to established hash table */
- ret = __dccp_v6_check_established(sk, snum, NULL);
-out:
- local_bh_enable();
- return ret;
- }
-}
-
static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
@@ -403,7 +234,7 @@ static int dccp_v6_connect(struct sock *
inet->dport = usin->sin6_port;
dccp_set_state(sk, DCCP_REQUESTING);
- err = dccp_v6_hash_connect(sk);
+ err = inet6_hash_connect(&dccp_death_row, sk);
if (err)
goto late_failure;
/* FIXME */
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 01d5f46..4154f3a 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -5,7 +5,8 @@
*
* Generic INET6 transport hashtables
*
- * Authors: Lotsa people, from code originally in tcp
+ * Authors: Lotsa people, from code originally in tcp, generalised here
+ * by Arnaldo Carvalho de Melo <acme@mandriva.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -14,12 +15,13 @@
*/
#include <linux/config.h>
-
#include <linux/module.h>
+#include <linux/random.h>
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
+#include <net/ip.h>
struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
const struct in6_addr *daddr,
@@ -79,3 +81,180 @@ struct sock *inet6_lookup(struct inet_ha
}
EXPORT_SYMBOL_GPL(inet6_lookup);
+
+static int __inet6_check_established(struct inet_timewait_death_row *death_row,
+ struct sock *sk, const __u16 lport,
+ struct inet_timewait_sock **twp)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ const struct inet_sock *inet = inet_sk(sk);
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ const struct in6_addr *daddr = &np->rcv_saddr;
+ const struct in6_addr *saddr = &np->daddr;
+ const int dif = sk->sk_bound_dev_if;
+ const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
+ const unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr,
+ inet->dport);
+ struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
+ struct sock *sk2;
+ const struct hlist_node *node;
+ struct inet_timewait_sock *tw;
+
+ prefetch(head->chain.first);
+ write_lock(&head->lock);
+
+ /* Check TIME-WAIT sockets first. */
+ sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) {
+ const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
+
+ tw = inet_twsk(sk2);
+
+ if(*((__u32 *)&(tw->tw_dport)) == ports &&
+ sk2->sk_family == PF_INET6 &&
+ ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
+ ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
+ sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
+ if (twsk_unique(sk, sk2, twp))
+ goto unique;
+ else
+ goto not_unique;
+ }
+ }
+ tw = NULL;
+
+ /* And established part... */
+ sk_for_each(sk2, node, &head->chain) {
+ if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
+ goto not_unique;
+ }
+
+unique:
+ BUG_TRAP(sk_unhashed(sk));
+ __sk_add_node(sk, &head->chain);
+ sk->sk_hash = hash;
+ sock_prot_inc_use(sk->sk_prot);
+ write_unlock(&head->lock);
+
+ if (twp != NULL) {
+ *twp = tw;
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+ } else if (tw != NULL) {
+ /* Silly. Should hash-dance instead... */
+ inet_twsk_deschedule(tw, death_row);
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+
+ inet_twsk_put(tw);
+ }
+ return 0;
+
+not_unique:
+ write_unlock(&head->lock);
+ return -EADDRNOTAVAIL;
+}
+
+static inline u32 inet6_sk_port_offset(const struct sock *sk)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ return secure_ipv6_port_ephemeral(np->rcv_saddr.s6_addr32,
+ np->daddr.s6_addr32,
+ inet->dport);
+}
+
+int inet6_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ const unsigned short snum = inet_sk(sk)->num;
+ struct inet_bind_hashbucket *head;
+ struct inet_bind_bucket *tb;
+ int ret;
+
+ if (snum == 0) {
+ const int low = sysctl_local_port_range[0];
+ const int high = sysctl_local_port_range[1];
+ const int range = high - low;
+ int i, port;
+ static u32 hint;
+ const u32 offset = hint + inet6_sk_port_offset(sk);
+ struct hlist_node *node;
+ struct inet_timewait_sock *tw = NULL;
+
+ local_bh_disable();
+ for (i = 1; i <= range; i++) {
+ port = low + (i + offset) % range;
+ head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
+ spin_lock(&head->lock);
+
+ /* Does not bother with rcv_saddr checks,
+ * because the established check is already
+ * unique enough.
+ */
+ inet_bind_bucket_for_each(tb, node, &head->chain) {
+ if (tb->port == port) {
+ BUG_TRAP(!hlist_empty(&tb->owners));
+ if (tb->fastreuse >= 0)
+ goto next_port;
+ if (!__inet6_check_established(death_row,
+ sk, port,
+ &tw))
+ goto ok;
+ goto next_port;
+ }
+ }
+
+ tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
+ head, port);
+ if (!tb) {
+ spin_unlock(&head->lock);
+ break;
+ }
+ tb->fastreuse = -1;
+ goto ok;
+
+ next_port:
+ spin_unlock(&head->lock);
+ }
+ local_bh_enable();
+
+ return -EADDRNOTAVAIL;
+
+ok:
+ hint += i;
+
+ /* Head lock still held and bh's disabled */
+ inet_bind_hash(sk, tb, port);
+ if (sk_unhashed(sk)) {
+ inet_sk(sk)->sport = htons(port);
+ __inet6_hash(hinfo, sk);
+ }
+ spin_unlock(&head->lock);
+
+ if (tw) {
+ inet_twsk_deschedule(tw, death_row);
+ inet_twsk_put(tw);
+ }
+
+ ret = 0;
+ goto out;
+ }
+
+ head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
+ tb = inet_csk(sk)->icsk_bind_hash;
+ spin_lock_bh(&head->lock);
+
+ if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
+ __inet6_hash(hinfo, sk);
+ spin_unlock_bh(&head->lock);
+ return 0;
+ } else {
+ spin_unlock(&head->lock);
+ /* No definite answer... Walk to established hash table */
+ ret = __inet6_check_established(death_row, sk, snum, NULL);
+out:
+ local_bh_enable();
+ return ret;
+ }
+}
+
+EXPORT_SYMBOL_GPL(inet6_hash_connect);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 7a077de..1587c35 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -119,177 +119,6 @@ static __u32 tcp_v6_init_sequence(struct
}
}
-static int __tcp_v6_check_established(struct sock *sk, const __u16 lport,
- struct inet_timewait_sock **twp)
-{
- struct inet_sock *inet = inet_sk(sk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
- const struct in6_addr *daddr = &np->rcv_saddr;
- const struct in6_addr *saddr = &np->daddr;
- const int dif = sk->sk_bound_dev_if;
- const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
- unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr, inet->dport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
- struct sock *sk2;
- const struct hlist_node *node;
- struct inet_timewait_sock *tw;
-
- prefetch(head->chain.first);
- write_lock(&head->lock);
-
- /* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
- const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
-
- tw = inet_twsk(sk2);
-
- if(*((__u32 *)&(tw->tw_dport)) == ports &&
- sk2->sk_family == PF_INET6 &&
- ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
- ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
- sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
- if (twsk_unique(sk, sk2, twp))
- goto unique;
- else
- goto not_unique;
- }
- }
- tw = NULL;
-
- /* And established part... */
- sk_for_each(sk2, node, &head->chain) {
- if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
- goto not_unique;
- }
-
-unique:
- BUG_TRAP(sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
- sk->sk_hash = hash;
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(&head->lock);
-
- if (twp) {
- *twp = tw;
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
- } else if (tw) {
- /* Silly. Should hash-dance instead... */
- inet_twsk_deschedule(tw, &tcp_death_row);
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
- inet_twsk_put(tw);
- }
- return 0;
-
-not_unique:
- write_unlock(&head->lock);
- return -EADDRNOTAVAIL;
-}
-
-static inline u32 tcpv6_port_offset(const struct sock *sk)
-{
- const struct inet_sock *inet = inet_sk(sk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
-
- return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
- np->daddr.s6_addr32,
- inet->dport);
-}
-
-static int tcp_v6_hash_connect(struct sock *sk)
-{
- unsigned short snum = inet_sk(sk)->num;
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- int ret;
-
- if (!snum) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int range = high - low;
- int i;
- int port;
- static u32 hint;
- u32 offset = hint + tcpv6_port_offset(sk);
- struct hlist_node *node;
- struct inet_timewait_sock *tw = NULL;
-
- local_bh_disable();
- for (i = 1; i <= range; i++) {
- port = low + (i + offset) % range;
- head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
-
- /* Does not bother with rcv_saddr checks,
- * because the established check is already
- * unique enough.
- */
- inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->port == port) {
- BUG_TRAP(!hlist_empty(&tb->owners));
- if (tb->fastreuse >= 0)
- goto next_port;
- if (!__tcp_v6_check_established(sk,
- port,
- &tw))
- goto ok;
- goto next_port;
- }
- }
-
- tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
- if (!tb) {
- spin_unlock(&head->lock);
- break;
- }
- tb->fastreuse = -1;
- goto ok;
-
- next_port:
- spin_unlock(&head->lock);
- }
- local_bh_enable();
-
- return -EADDRNOTAVAIL;
-
-ok:
- hint += i;
-
- /* Head lock still held and bh's disabled */
- inet_bind_hash(sk, tb, port);
- if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(port);
- __inet6_hash(&tcp_hashinfo, sk);
- }
- spin_unlock(&head->lock);
-
- if (tw) {
- inet_twsk_deschedule(tw, &tcp_death_row);
- inet_twsk_put(tw);
- }
-
- ret = 0;
- goto out;
- }
-
- head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
- tb = inet_csk(sk)->icsk_bind_hash;
- spin_lock_bh(&head->lock);
-
- if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
- __inet6_hash(&tcp_hashinfo, sk);
- spin_unlock_bh(&head->lock);
- return 0;
- } else {
- spin_unlock(&head->lock);
- /* No definite answer... Walk to established hash table */
- ret = __tcp_v6_check_established(sk, snum, NULL);
-out:
- local_bh_enable();
- return ret;
- }
-}
-
static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
@@ -450,7 +279,7 @@ static int tcp_v6_connect(struct sock *s
inet->dport = usin->sin6_port;
tcp_set_state(sk, TCP_SYN_SENT);
- err = tcp_v6_hash_connect(sk);
+ err = inet6_hash_connect(&tcp_death_row, sk);
if (err)
goto late_failure;
^ permalink raw reply related
* Re: [PATCH] tcp: make reno default for mortals
From: Arnaldo Carvalho de Melo @ 2005-12-12 14:34 UTC (permalink / raw)
To: dccp
On 12/12/05, David S. Miller <davem@davemloft.net> wrote:
> From: "David S. Miller" <davem@davemloft.net>
> Date: Tue, 06 Dec 2005 21:08:27 -0800 (PST)
>
> > Then we shouldn't be changing BIC, but instead make a new
> > tcp_cubic.c :-)
>
> As I mentioned in my net-2.6.16 rebase posting tonight,
> I took care of this.
Great, soon the DCCP club band (growing, whee!) will join the
anti-congestion brigade and add another algo to the tree, CCID2,
TCP-Like congestion control for DCCP, courtesy of Andrea Bittau, the
DCCPer of the month </McDonalds> :-)
- Arnaldo (happy clown 8-) )
^ permalink raw reply
* [PATCH 5/5][TCPv6]: Fix skb leak
From: Arnaldo Carvalho de Melo @ 2005-12-12 1:18 UTC (permalink / raw)
To: dccp
[-- Attachment #1: Type: text/plain, Size: 143 bytes --]
Hi David,
Please consider pulling from:
master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
Best Regards,
- Arnaldo
[-- Attachment #2: 5.patch --]
[-- Type: text/x-patch, Size: 1822 bytes --]
tree 402c2b51d32d31f999fed91365c509d1fbd355c7
parent 9b0c0171dae84125f43b6ca80f9108768863df97
author Arnaldo Carvalho de Melo <acme@mandriva.com> 1134349890 -0200
committer Arnaldo Carvalho de Melo <acme@mandriva.com> 1134349890 -0200
[TCPv6]: Fix skb leak
Spotted by Francois Romieu, thanks! The DCCP case was fixed as
well in another patch.
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
------------------------------------------------------------------------------
tcp_ipv6.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
------------------------------------------------------------------------------
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 7a077de..514b57b 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -779,13 +779,12 @@ static void tcp_v6_send_reset(struct sk_
/* sk = NULL, but it is safe for now. RST socket required. */
if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
- if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0)
+ if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
+ ip6_xmit(NULL, buff, &fl, NULL, 0);
+ TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
+ TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
return;
-
- ip6_xmit(NULL, buff, &fl, NULL, 0);
- TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
- TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
- return;
+ }
}
kfree_skb(buff);
@@ -844,11 +843,11 @@ static void tcp_v6_send_ack(struct sk_bu
fl.fl_ip_sport = t1->source;
if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
- if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0)
+ if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
+ ip6_xmit(NULL, buff, &fl, NULL, 0);
+ TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
return;
- ip6_xmit(NULL, buff, &fl, NULL, 0);
- TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
- return;
+ }
}
kfree_skb(buff);
^ permalink raw reply related
* [PATCH 1/4][FIXED][DCCP]: Introduce DCCPv6
From: Arnaldo Carvalho de Melo @ 2005-12-12 1:17 UTC (permalink / raw)
To: dccp
[-- Attachment #1: Type: text/plain, Size: 345 bytes --]
Hi David,
Please consider pulling from:
master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
This one has a fix for the problem spotted by Francois, next message will
have the fix for tcp_v6_send_{ack,reset}, the origin of this problem,
[2..4/4] are the same as last series, as we chatted.
Best Regards,
- Arnaldo
[-- Attachment #2: 1.patch --]
[-- Type: text/x-patch, Size: 41309 bytes --]
tree 3be0bc7ded1550ae7c33f192760410df6536c176
parent c084c1a9a00a5d872fa19d4fc206e996dc582c44
author Arnaldo Carvalho de Melo <acme@mandriva.com> 1134349305 -0200
committer Arnaldo Carvalho de Melo <acme@mandriva.com> 1134349305 -0200
[DCCP]: Introduce DCCPv6
Still needs mucho polishing, specially in the checksum code, but works just
fine, inet_diag/iproute2 and all 8)
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
------------------------------------------------------------------------------
Makefile | 4
ipv6.c | 1438 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ipv6.h | 37 +
minisocks.c | 13
4 files changed, 1491 insertions(+), 1 deletion(-)
------------------------------------------------------------------------------
diff --git a/net/dccp/Makefile b/net/dccp/Makefile
index 344a8da..87b27ff 100644
--- a/net/dccp/Makefile
+++ b/net/dccp/Makefile
@@ -1,3 +1,7 @@
+obj-$(CONFIG_IPV6) += dccp_ipv6.o
+
+dccp_ipv6-y := ipv6.o
+
obj-$(CONFIG_IP_DCCP) += dccp.o
dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o \
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
new file mode 100644
index 0000000..a7d2aee
--- /dev/null
+++ b/net/dccp/ipv6.c
@@ -0,0 +1,1438 @@
+/*
+ * DCCP over IPv6
+ * Linux INET6 implementation
+ *
+ * Based on net/dccp6/ipv6.c
+ *
+ * Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/xfrm.h>
+
+#include <net/addrconf.h>
+#include <net/inet_common.h>
+#include <net/inet_hashtables.h>
+#include <net/inet6_connection_sock.h>
+#include <net/inet6_hashtables.h>
+#include <net/ip6_route.h>
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/xfrm.h>
+
+#include "dccp.h"
+#include "ipv6.h"
+
+static void dccp_v6_ctl_send_reset(struct sk_buff *skb);
+static void dccp_v6_reqsk_send_ack(struct sk_buff *skb,
+ struct request_sock *req);
+static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb);
+
+static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
+
+static struct inet_connection_sock_af_ops dccp_ipv6_mapped;
+static struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
+
+static int dccp_v6_get_port(struct sock *sk, unsigned short snum)
+{
+ return inet_csk_get_port(&dccp_hashinfo, sk, snum,
+ inet6_csk_bind_conflict);
+}
+
+static void dccp_v6_hash(struct sock *sk)
+{
+ if (sk->sk_state != DCCP_CLOSED) {
+ if (inet_csk(sk)->icsk_af_ops == &dccp_ipv6_mapped) {
+ dccp_prot.hash(sk);
+ return;
+ }
+ local_bh_disable();
+ __inet6_hash(&dccp_hashinfo, sk);
+ local_bh_enable();
+ }
+}
+
+static inline u16 dccp_v6_check(struct dccp_hdr *dh, int len,
+ struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ unsigned long base)
+{
+ return csum_ipv6_magic(saddr, daddr, len, IPPROTO_DCCP, base);
+}
+
+static __u32 dccp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
+{
+ const struct dccp_hdr *dh = dccp_hdr(skb);
+
+ if (skb->protocol == htons(ETH_P_IPV6))
+ return secure_tcpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32,
+ skb->nh.ipv6h->saddr.s6_addr32,
+ dh->dccph_dport,
+ dh->dccph_sport);
+ else
+ return secure_dccp_sequence_number(skb->nh.iph->daddr,
+ skb->nh.iph->saddr,
+ dh->dccph_dport,
+ dh->dccph_sport);
+}
+
+static int __dccp_v6_check_established(struct sock *sk, const __u16 lport,
+ struct inet_timewait_sock **twp)
+{
+ struct inet_sock *inet = inet_sk(sk);
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ const struct in6_addr *daddr = &np->rcv_saddr;
+ const struct in6_addr *saddr = &np->daddr;
+ const int dif = sk->sk_bound_dev_if;
+ const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
+ const unsigned int hash = inet6_ehashfn(daddr, inet->num,
+ saddr, inet->dport);
+ struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash);
+ struct sock *sk2;
+ const struct hlist_node *node;
+ struct inet_timewait_sock *tw;
+
+ prefetch(head->chain.first);
+ write_lock(&head->lock);
+
+ /* Check TIME-WAIT sockets first. */
+ sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) {
+ const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
+
+ tw = inet_twsk(sk2);
+
+ if(*((__u32 *)&(tw->tw_dport)) == ports &&
+ sk2->sk_family == PF_INET6 &&
+ ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
+ ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
+ sk2->sk_bound_dev_if == sk->sk_bound_dev_if)
+ goto not_unique;
+ }
+ tw = NULL;
+
+ /* And established part... */
+ sk_for_each(sk2, node, &head->chain) {
+ if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
+ goto not_unique;
+ }
+
+ BUG_TRAP(sk_unhashed(sk));
+ __sk_add_node(sk, &head->chain);
+ sk->sk_hash = hash;
+ sock_prot_inc_use(sk->sk_prot);
+ write_unlock(&head->lock);
+
+ if (twp) {
+ *twp = tw;
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+ } else if (tw) {
+ /* Silly. Should hash-dance instead... */
+ inet_twsk_deschedule(tw, &dccp_death_row);
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+
+ inet_twsk_put(tw);
+ }
+ return 0;
+
+not_unique:
+ write_unlock(&head->lock);
+ return -EADDRNOTAVAIL;
+}
+
+static inline u32 dccp_v6_port_offset(const struct sock *sk)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+
+ return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
+ np->daddr.s6_addr32,
+ inet->dport);
+}
+
+static int dccp_v6_hash_connect(struct sock *sk)
+{
+ const unsigned short snum = inet_sk(sk)->num;
+ struct inet_bind_hashbucket *head;
+ struct inet_bind_bucket *tb;
+ int ret;
+
+ if (snum == 0) {
+ int low = sysctl_local_port_range[0];
+ int high = sysctl_local_port_range[1];
+ int range = high - low;
+ int i;
+ int port;
+ static u32 hint;
+ u32 offset = hint + dccp_v6_port_offset(sk);
+ struct hlist_node *node;
+ struct inet_timewait_sock *tw = NULL;
+
+ local_bh_disable();
+ for (i = 1; i <= range; i++) {
+ port = low + (i + offset) % range;
+ head = &dccp_hashinfo.bhash[inet_bhashfn(port,
+ dccp_hashinfo.bhash_size)];
+ spin_lock(&head->lock);
+
+ /* Does not bother with rcv_saddr checks,
+ * because the established check is already
+ * unique enough.
+ */
+ inet_bind_bucket_for_each(tb, node, &head->chain) {
+ if (tb->port == port) {
+ BUG_TRAP(!hlist_empty(&tb->owners));
+ if (tb->fastreuse >= 0)
+ goto next_port;
+ if (!__dccp_v6_check_established(sk,
+ port,
+ &tw))
+ goto ok;
+ goto next_port;
+ }
+ }
+
+ tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep,
+ head, port);
+ if (!tb) {
+ spin_unlock(&head->lock);
+ break;
+ }
+ tb->fastreuse = -1;
+ goto ok;
+
+ next_port:
+ spin_unlock(&head->lock);
+ }
+ local_bh_enable();
+
+ return -EADDRNOTAVAIL;
+ok:
+ hint += i;
+
+ /* Head lock still held and bh's disabled */
+ inet_bind_hash(sk, tb, port);
+ if (sk_unhashed(sk)) {
+ inet_sk(sk)->sport = htons(port);
+ __inet6_hash(&dccp_hashinfo, sk);
+ }
+ spin_unlock(&head->lock);
+
+ if (tw) {
+ inet_twsk_deschedule(tw, &dccp_death_row);
+ inet_twsk_put(tw);
+ }
+
+ ret = 0;
+ goto out;
+ }
+
+ head = &dccp_hashinfo.bhash[inet_bhashfn(snum,
+ dccp_hashinfo.bhash_size)];
+ tb = inet_csk(sk)->icsk_bind_hash;
+ spin_lock_bh(&head->lock);
+
+ if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
+ __inet6_hash(&dccp_hashinfo, sk);
+ spin_unlock_bh(&head->lock);
+ return 0;
+ } else {
+ spin_unlock(&head->lock);
+ /* No definite answer... Walk to established hash table */
+ ret = __dccp_v6_check_established(sk, snum, NULL);
+out:
+ local_bh_enable();
+ return ret;
+ }
+}
+
+static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
+ int addr_len)
+{
+ struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct in6_addr *saddr = NULL, *final_p = NULL, final;
+ struct flowi fl;
+ struct dst_entry *dst;
+ int addr_type;
+ int err;
+
+ dp->dccps_role = DCCP_ROLE_CLIENT;
+
+ if (addr_len < SIN6_LEN_RFC2133)
+ return -EINVAL;
+
+ if (usin->sin6_family != AF_INET6)
+ return -EAFNOSUPPORT;
+
+ memset(&fl, 0, sizeof(fl));
+
+ if (np->sndflow) {
+ fl.fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
+ IP6_ECN_flow_init(fl.fl6_flowlabel);
+ if (fl.fl6_flowlabel & IPV6_FLOWLABEL_MASK) {
+ struct ip6_flowlabel *flowlabel;
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
+ fl6_sock_release(flowlabel);
+ }
+ }
+
+ /*
+ * connect() to INADDR_ANY means loopback (BSD'ism).
+ */
+
+ if (ipv6_addr_any(&usin->sin6_addr))
+ usin->sin6_addr.s6_addr[15] = 0x1;
+
+ addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+ if(addr_type & IPV6_ADDR_MULTICAST)
+ return -ENETUNREACH;
+
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ usin->sin6_scope_id) {
+ /* If interface is set while binding, indices
+ * must coincide.
+ */
+ if (sk->sk_bound_dev_if &&
+ sk->sk_bound_dev_if != usin->sin6_scope_id)
+ return -EINVAL;
+
+ sk->sk_bound_dev_if = usin->sin6_scope_id;
+ }
+
+ /* Connect to link-local address requires an interface */
+ if (!sk->sk_bound_dev_if)
+ return -EINVAL;
+ }
+
+ ipv6_addr_copy(&np->daddr, &usin->sin6_addr);
+ np->flow_label = fl.fl6_flowlabel;
+
+ /*
+ * DCCP over IPv4
+ */
+
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ u32 exthdrlen = dp->dccps_ext_header_len;
+ struct sockaddr_in sin;
+
+ SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
+
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = usin->sin6_port;
+ sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
+
+ inet_csk(sk)->icsk_af_ops = &dccp_ipv6_mapped;
+ sk->sk_backlog_rcv = dccp_v4_do_rcv;
+
+ err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
+
+ if (err) {
+ dp->dccps_ext_header_len = exthdrlen;
+ inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops;
+ sk->sk_backlog_rcv = dccp_v6_do_rcv;
+ goto failure;
+ } else {
+ ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF),
+ inet->saddr);
+ ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF),
+ inet->rcv_saddr);
+ }
+
+ return err;
+ }
+
+ if (!ipv6_addr_any(&np->rcv_saddr))
+ saddr = &np->rcv_saddr;
+
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, saddr ? saddr : &np->saddr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = usin->sin6_port;
+ fl.fl_ip_sport = inet->sport;
+
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+
+ err = ip6_dst_lookup(sk, &dst, &fl);
+ if (err)
+ goto failure;
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto failure;
+
+ if (saddr == NULL) {
+ saddr = &fl.fl6_src;
+ ipv6_addr_copy(&np->rcv_saddr, saddr);
+ }
+
+ /* set the source address */
+ ipv6_addr_copy(&np->saddr, saddr);
+ inet->rcv_saddr = LOOPBACK4_IPV6;
+
+ ip6_dst_store(sk, dst, NULL);
+
+ dp->dccps_ext_header_len = 0;
+ if (np->opt)
+ dp->dccps_ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+
+ inet->dport = usin->sin6_port;
+
+ dccp_set_state(sk, DCCP_REQUESTING);
+ err = dccp_v6_hash_connect(sk);
+ if (err)
+ goto late_failure;
+ /* FIXME */
+#if 0
+ dp->dccps_gar = secure_dccp_v6_sequence_number(np->saddr.s6_addr32,
+ np->daddr.s6_addr32,
+ inet->sport,
+ inet->dport);
+#endif
+ err = dccp_connect(sk);
+ if (err)
+ goto late_failure;
+
+ return 0;
+
+late_failure:
+ dccp_set_state(sk, DCCP_CLOSED);
+ __sk_dst_reset(sk);
+failure:
+ inet->dport = 0;
+ sk->sk_route_caps = 0;
+ return err;
+}
+
+static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info)
+{
+ struct ipv6hdr *hdr = (struct ipv6hdr *)skb->data;
+ const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
+ struct ipv6_pinfo *np;
+ struct sock *sk;
+ int err;
+ __u64 seq;
+
+ sk = inet6_lookup(&dccp_hashinfo, &hdr->daddr, dh->dccph_dport,
+ &hdr->saddr, dh->dccph_sport, skb->dev->ifindex);
+
+ if (sk == NULL) {
+ ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), ICMP6_MIB_INERRORS);
+ return;
+ }
+
+ if (sk->sk_state == DCCP_TIME_WAIT) {
+ inet_twsk_put((struct inet_timewait_sock *)sk);
+ return;
+ }
+
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk))
+ NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
+
+ if (sk->sk_state == DCCP_CLOSED)
+ goto out;
+
+ np = inet6_sk(sk);
+
+ if (type == ICMPV6_PKT_TOOBIG) {
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct dst_entry *dst = NULL;
+
+ if (sock_owned_by_user(sk))
+ goto out;
+ if ((1 << sk->sk_state) & (DCCPF_LISTEN | DCCPF_CLOSED))
+ goto out;
+
+ /* icmp should have updated the destination cache entry */
+ dst = __sk_dst_check(sk, np->dst_cookie);
+
+ if (dst == NULL) {
+ struct inet_sock *inet = inet_sk(sk);
+ struct flowi fl;
+
+ /* BUGGG_FUTURE: Again, it is not clear how
+ to handle rthdr case. Ignore this complexity
+ for now.
+ */
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet->dport;
+ fl.fl_ip_sport = inet->sport;
+
+ if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
+ sk->sk_err_soft = -err;
+ goto out;
+ }
+
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ sk->sk_err_soft = -err;
+ goto out;
+ }
+
+ } else
+ dst_hold(dst);
+
+ if (dp->dccps_pmtu_cookie > dst_mtu(dst)) {
+ dccp_sync_mss(sk, dst_mtu(dst));
+ } /* else let the usual retransmit timer handle it */
+ dst_release(dst);
+ goto out;
+ }
+
+ icmpv6_err_convert(type, code, &err);
+
+ seq = DCCP_SKB_CB(skb)->dccpd_seq;
+ /* Might be for an request_sock */
+ switch (sk->sk_state) {
+ struct request_sock *req, **prev;
+ case DCCP_LISTEN:
+ if (sock_owned_by_user(sk))
+ goto out;
+
+ req = inet6_csk_search_req(sk, &prev, dh->dccph_dport,
+ &hdr->daddr, &hdr->saddr,
+ inet6_iif(skb));
+ if (!req)
+ goto out;
+
+ /* ICMPs are not backlogged, hence we cannot get
+ * an established socket here.
+ */
+ BUG_TRAP(req->sk == NULL);
+
+ if (seq != dccp_rsk(req)->dreq_iss) {
+ NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+ goto out;
+ }
+
+ inet_csk_reqsk_queue_drop(sk, req, prev);
+ goto out;
+
+ case DCCP_REQUESTING:
+ case DCCP_RESPOND: /* Cannot happen.
+ It can, it SYNs are crossed. --ANK */
+ if (!sock_owned_by_user(sk)) {
+ DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
+ sk->sk_err = err;
+ /*
+ * Wake people up to see the error
+ * (see connect in sock.c)
+ */
+ sk->sk_error_report(sk);
+
+ dccp_done(sk);
+ } else
+ sk->sk_err_soft = err;
+ goto out;
+ }
+
+ if (!sock_owned_by_user(sk) && np->recverr) {
+ sk->sk_err = err;
+ sk->sk_error_report(sk);
+ } else
+ sk->sk_err_soft = err;
+
+out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
+}
+
+
+static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
+ struct dst_entry *dst)
+{
+ struct inet6_request_sock *ireq6 = inet6_rsk(req);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct sk_buff *skb;
+ struct ipv6_txoptions *opt = NULL;
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+ int err = -1;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.fl6_flowlabel = 0;
+ fl.oif = ireq6->iif;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+
+ if (dst == NULL) {
+ opt = np->opt;
+ if (opt == NULL &&
+ np->rxopt.bits.osrcrt == 2 &&
+ ireq6->pktopts) {
+ struct sk_buff *pktopts = ireq6->pktopts;
+ struct inet6_skb_parm *rxopt = IP6CB(pktopts);
+ if (rxopt->srcrt)
+ opt = ipv6_invert_rthdr(sk,
+ (struct ipv6_rt_hdr *)(pktopts->nh.raw +
+ rxopt->srcrt));
+ }
+
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *)opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+
+ err = ip6_dst_lookup(sk, &dst, &fl);
+ if (err)
+ goto done;
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto done;
+ }
+
+ skb = dccp_make_response(sk, dst, req);
+ if (skb != NULL) {
+ struct dccp_hdr *dh = dccp_hdr(skb);
+ dh->dccph_checksum = dccp_v6_check(dh, skb->len,
+ &ireq6->loc_addr,
+ &ireq6->rmt_addr,
+ csum_partial((char *)dh,
+ skb->len,
+ skb->csum));
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ err = ip6_xmit(sk, skb, &fl, opt, 0);
+ if (err == NET_XMIT_CN)
+ err = 0;
+ }
+
+done:
+ if (opt && opt != np->opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ return err;
+}
+
+static void dccp_v6_reqsk_destructor(struct request_sock *req)
+{
+ if (inet6_rsk(req)->pktopts != NULL)
+ kfree_skb(inet6_rsk(req)->pktopts);
+}
+
+static struct request_sock_ops dccp6_request_sock_ops = {
+ .family = AF_INET6,
+ .obj_size = sizeof(struct dccp6_request_sock),
+ .rtx_syn_ack = dccp_v6_send_response,
+ .send_ack = dccp_v6_reqsk_send_ack,
+ .destructor = dccp_v6_reqsk_destructor,
+ .send_reset = dccp_v6_ctl_send_reset,
+};
+
+static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct dccp_hdr *dh = dccp_hdr(skb);
+
+ dh->dccph_checksum = csum_ipv6_magic(&np->saddr, &np->daddr,
+ len, IPPROTO_DCCP,
+ csum_partial((char *)dh,
+ dh->dccph_doff << 2,
+ skb->csum));
+}
+
+static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
+{
+ struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
+ const int dccp_hdr_reset_len = sizeof(struct dccp_hdr) +
+ sizeof(struct dccp_hdr_ext) +
+ sizeof(struct dccp_hdr_reset);
+ struct sk_buff *skb;
+ struct flowi fl;
+ u64 seqno;
+
+ if (rxdh->dccph_type == DCCP_PKT_RESET)
+ return;
+
+ if (!ipv6_unicast_destination(rxskb))
+ return;
+
+ /*
+ * We need to grab some memory, and put together an RST,
+ * and then put it into the queue to be sent.
+ */
+
+ skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_reset_len, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_reset_len);
+
+ skb->h.raw = skb_push(skb, dccp_hdr_reset_len);
+ dh = dccp_hdr(skb);
+ memset(dh, 0, dccp_hdr_reset_len);
+
+ /* Swap the send and the receive. */
+ dh->dccph_type = DCCP_PKT_RESET;
+ dh->dccph_sport = rxdh->dccph_dport;
+ dh->dccph_dport = rxdh->dccph_sport;
+ dh->dccph_doff = dccp_hdr_reset_len / 4;
+ dh->dccph_x = 1;
+ dccp_hdr_reset(skb)->dccph_reset_code =
+ DCCP_SKB_CB(rxskb)->dccpd_reset_code;
+
+ /* See "8.3.1. Abnormal Termination" in draft-ietf-dccp-spec-11 */
+ seqno = 0;
+ if (DCCP_SKB_CB(rxskb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
+ dccp_set_seqno(&seqno, DCCP_SKB_CB(rxskb)->dccpd_ack_seq + 1);
+
+ dccp_hdr_set_seq(dh, seqno);
+ dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
+ DCCP_SKB_CB(rxskb)->dccpd_seq);
+
+ memset(&fl, 0, sizeof(fl));
+ ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr);
+ dh->dccph_checksum = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,
+ sizeof(*dh), IPPROTO_DCCP,
+ skb->csum);
+ fl.proto = IPPROTO_DCCP;
+ fl.oif = inet6_iif(rxskb);
+ fl.fl_ip_dport = dh->dccph_dport;
+ fl.fl_ip_sport = dh->dccph_sport;
+
+ /* sk = NULL, but it is safe for now. RST socket required. */
+ if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
+ if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
+ ip6_xmit(NULL, skb, &fl, NULL, 0);
+ DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
+ DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS);
+ return;
+ }
+ }
+
+ kfree_skb(skb);
+}
+
+static void dccp_v6_ctl_send_ack(struct sk_buff *rxskb)
+{
+ struct flowi fl;
+ struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
+ const int dccp_hdr_ack_len = sizeof(struct dccp_hdr) +
+ sizeof(struct dccp_hdr_ext) +
+ sizeof(struct dccp_hdr_ack_bits);
+ struct sk_buff *skb;
+
+ skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_ack_len, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_ack_len);
+
+ skb->h.raw = skb_push(skb, dccp_hdr_ack_len);
+ dh = dccp_hdr(skb);
+ memset(dh, 0, dccp_hdr_ack_len);
+
+ /* Build DCCP header and checksum it. */
+ dh->dccph_type = DCCP_PKT_ACK;
+ dh->dccph_sport = rxdh->dccph_dport;
+ dh->dccph_dport = rxdh->dccph_sport;
+ dh->dccph_doff = dccp_hdr_ack_len / 4;
+ dh->dccph_x = 1;
+
+ dccp_hdr_set_seq(dh, DCCP_SKB_CB(rxskb)->dccpd_ack_seq);
+ dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
+ DCCP_SKB_CB(rxskb)->dccpd_seq);
+
+ memset(&fl, 0, sizeof(fl));
+ ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr);
+
+ /* FIXME: calculate checksum, IPv4 also should... */
+
+ fl.proto = IPPROTO_DCCP;
+ fl.oif = inet6_iif(rxskb);
+ fl.fl_ip_dport = dh->dccph_dport;
+ fl.fl_ip_sport = dh->dccph_sport;
+
+ if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
+ if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
+ ip6_xmit(NULL, skb, &fl, NULL, 0);
+ DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
+ return;
+ }
+ }
+
+ kfree_skb(skb);
+}
+
+static void dccp_v6_reqsk_send_ack(struct sk_buff *skb,
+ struct request_sock *req)
+{
+ dccp_v6_ctl_send_ack(skb);
+}
+
+static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
+{
+ const struct dccp_hdr *dh = dccp_hdr(skb);
+ const struct ipv6hdr *iph = skb->nh.ipv6h;
+ struct sock *nsk;
+ struct request_sock **prev;
+ /* Find possible connection requests. */
+ struct request_sock *req = inet6_csk_search_req(sk, &prev,
+ dh->dccph_sport,
+ &iph->saddr,
+ &iph->daddr,
+ inet6_iif(skb));
+ if (req != NULL)
+ return dccp_check_req(sk, skb, req, prev);
+
+ nsk = __inet6_lookup_established(&dccp_hashinfo,
+ &iph->saddr, dh->dccph_sport,
+ &iph->daddr, ntohs(dh->dccph_dport),
+ inet6_iif(skb));
+
+ if (nsk != NULL) {
+ if (nsk->sk_state != DCCP_TIME_WAIT) {
+ bh_lock_sock(nsk);
+ return nsk;
+ }
+ inet_twsk_put((struct inet_timewait_sock *)nsk);
+ return NULL;
+ }
+
+ return sk;
+}
+
+static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
+{
+ struct inet_request_sock *ireq;
+ struct dccp_sock dp;
+ struct request_sock *req;
+ struct dccp_request_sock *dreq;
+ struct inet6_request_sock *ireq6;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ const __u32 service = dccp_hdr_request(skb)->dccph_req_service;
+ struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
+ __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
+
+ if (skb->protocol == htons(ETH_P_IP))
+ return dccp_v4_conn_request(sk, skb);
+
+ if (!ipv6_unicast_destination(skb))
+ goto drop;
+
+ if (dccp_bad_service_code(sk, service)) {
+ reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
+ goto drop;
+ }
+ /*
+ * There are no SYN attacks on IPv6, yet...
+ */
+ if (inet_csk_reqsk_queue_is_full(sk))
+ goto drop;
+
+ if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
+ goto drop;
+
+ req = inet6_reqsk_alloc(sk->sk_prot->rsk_prot);
+ if (req == NULL)
+ goto drop;
+
+ /* FIXME: process options */
+
+ dccp_openreq_init(req, &dp, skb);
+
+ ireq6 = inet6_rsk(req);
+ ireq = inet_rsk(req);
+ ipv6_addr_copy(&ireq6->rmt_addr, &skb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&ireq6->loc_addr, &skb->nh.ipv6h->daddr);
+ req->rcv_wnd = 100; /* Fake, option parsing will get the
+ right value */
+ ireq6->pktopts = NULL;
+
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ ireq6->pktopts = skb;
+ }
+ ireq6->iif = sk->sk_bound_dev_if;
+
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ ireq6->iif = inet6_iif(skb);
+
+ /*
+ * Step 3: Process LISTEN state
+ *
+ * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
+ *
+ * In fact we defer setting S.GSR, S.SWL, S.SWH to
+ * dccp_create_openreq_child.
+ */
+ dreq = dccp_rsk(req);
+ dreq->dreq_isr = dcb->dccpd_seq;
+ dreq->dreq_iss = dccp_v6_init_sequence(sk, skb);
+ dreq->dreq_service = service;
+
+ if (dccp_v6_send_response(sk, req, NULL))
+ goto drop_and_free;
+
+ inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+ return 0;
+
+drop_and_free:
+ reqsk_free(req);
+drop:
+ DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
+ dcb->dccpd_reset_code = reset_code;
+ return -1;
+}
+
+static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
+ struct sk_buff *skb,
+ struct request_sock *req,
+ struct dst_entry *dst)
+{
+ struct inet6_request_sock *ireq6 = inet6_rsk(req);
+ struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
+ struct inet_sock *newinet;
+ struct dccp_sock *newdp;
+ struct dccp6_sock *newdp6;
+ struct sock *newsk;
+ struct ipv6_txoptions *opt;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ /*
+ * v6 mapped
+ */
+
+ newsk = dccp_v4_request_recv_sock(sk, skb, req, dst);
+ if (newsk == NULL)
+ return NULL;
+
+ newdp6 = (struct dccp6_sock *)newsk;
+ newdp = dccp_sk(newsk);
+ newinet = inet_sk(newsk);
+ newinet->pinet6 = &newdp6->inet6;
+ newnp = inet6_sk(newsk);
+
+ memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+
+ ipv6_addr_set(&newnp->daddr, 0, 0, htonl(0x0000FFFF),
+ newinet->daddr);
+
+ ipv6_addr_set(&newnp->saddr, 0, 0, htonl(0x0000FFFF),
+ newinet->saddr);
+
+ ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
+
+ inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped;
+ newsk->sk_backlog_rcv = dccp_v4_do_rcv;
+ newnp->pktoptions = NULL;
+ newnp->opt = NULL;
+ newnp->mcast_oif = inet6_iif(skb);
+ newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
+
+ /*
+ * No need to charge this sock to the relevant IPv6 refcnt debug socks count
+ * here, dccp_create_openreq_child now does this for us, see the comment in
+ * that function for the gory details. -acme
+ */
+
+ /* It is tricky place. Until this moment IPv4 tcp
+ worked with IPv6 icsk.icsk_af_ops.
+ Sync it now.
+ */
+ dccp_sync_mss(newsk, newdp->dccps_pmtu_cookie);
+
+ return newsk;
+ }
+
+ opt = np->opt;
+
+ if (sk_acceptq_is_full(sk))
+ goto out_overflow;
+
+ if (np->rxopt.bits.osrcrt == 2 &&
+ opt == NULL && ireq6->pktopts) {
+ struct inet6_skb_parm *rxopt = IP6CB(ireq6->pktopts);
+ if (rxopt->srcrt)
+ opt = ipv6_invert_rthdr(sk,
+ (struct ipv6_rt_hdr *)(ireq6->pktopts->nh.raw +
+ rxopt->srcrt));
+ }
+
+ if (dst == NULL) {
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+
+ if (ip6_dst_lookup(sk, &dst, &fl))
+ goto out;
+
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+ if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto out;
+ }
+
+ newsk = dccp_create_openreq_child(sk, req, skb);
+ if (newsk == NULL)
+ goto out;
+
+ /*
+ * No need to charge this sock to the relevant IPv6 refcnt debug socks
+ * count here, dccp_create_openreq_child now does this for us, see the
+ * comment in that function for the gory details. -acme
+ */
+
+ ip6_dst_store(newsk, dst, NULL);
+ newsk->sk_route_caps = dst->dev->features &
+ ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
+
+ newdp6 = (struct dccp6_sock *)newsk;
+ newinet = inet_sk(newsk);
+ newinet->pinet6 = &newdp6->inet6;
+ newdp = dccp_sk(newsk);
+ newnp = inet6_sk(newsk);
+
+ memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+
+ ipv6_addr_copy(&newnp->daddr, &ireq6->rmt_addr);
+ ipv6_addr_copy(&newnp->saddr, &ireq6->loc_addr);
+ ipv6_addr_copy(&newnp->rcv_saddr, &ireq6->loc_addr);
+ newsk->sk_bound_dev_if = ireq6->iif;
+
+ /* Now IPv6 options...
+
+ First: no IPv4 options.
+ */
+ newinet->opt = NULL;
+
+ /* Clone RX bits */
+ newnp->rxopt.all = np->rxopt.all;
+
+ /* Clone pktoptions received with SYN */
+ newnp->pktoptions = NULL;
+ if (ireq6->pktopts != NULL) {
+ newnp->pktoptions = skb_clone(ireq6->pktopts, GFP_ATOMIC);
+ kfree_skb(ireq6->pktopts);
+ ireq6->pktopts = NULL;
+ if (newnp->pktoptions)
+ skb_set_owner_r(newnp->pktoptions, newsk);
+ }
+ newnp->opt = NULL;
+ newnp->mcast_oif = inet6_iif(skb);
+ newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
+
+ /* Clone native IPv6 options from listening socket (if any)
+
+ Yes, keeping reference count would be much more clever,
+ but we make one more one thing there: reattach optmem
+ to newsk.
+ */
+ if (opt) {
+ newnp->opt = ipv6_dup_options(newsk, opt);
+ if (opt != np->opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ }
+
+ newdp->dccps_ext_header_len = 0;
+ if (newnp->opt)
+ newdp->dccps_ext_header_len = newnp->opt->opt_nflen +
+ newnp->opt->opt_flen;
+
+ dccp_sync_mss(newsk, dst_mtu(dst));
+
+ newinet->daddr = newinet->saddr = newinet->rcv_saddr = LOOPBACK4_IPV6;
+
+ __inet6_hash(&dccp_hashinfo, newsk);
+ inet_inherit_port(&dccp_hashinfo, sk, newsk);
+
+ return newsk;
+
+out_overflow:
+ NET_INC_STATS_BH(LINUX_MIB_LISTENOVERFLOWS);
+out:
+ NET_INC_STATS_BH(LINUX_MIB_LISTENDROPS);
+ if (opt && opt != np->opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ dst_release(dst);
+ return NULL;
+}
+
+/* The socket must have it's spinlock held when we get
+ * here.
+ *
+ * We have a potential double-lock case here, so even when
+ * doing backlog processing we use the BH locking scheme.
+ * This is because we cannot sleep with the original spinlock
+ * held.
+ */
+static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct sk_buff *opt_skb = NULL;
+
+ /* Imagine: socket is IPv6. IPv4 packet arrives,
+ goes to IPv4 receive handler and backlogged.
+ From backlog it always goes here. Kerboom...
+ Fortunately, dccp_rcv_established and rcv_established
+ handle them correctly, but it is not case with
+ dccp_v6_hnd_req and dccp_v6_ctl_send_reset(). --ANK
+ */
+
+ if (skb->protocol == htons(ETH_P_IP))
+ return dccp_v4_do_rcv(sk, skb);
+
+ if (sk_filter(sk, skb, 0))
+ goto discard;
+
+ /*
+ * socket locking is here for SMP purposes as backlog rcv
+ * is currently called with bh processing disabled.
+ */
+
+ /* Do Stevens' IPV6_PKTOPTIONS.
+
+ Yes, guys, it is the only place in our code, where we
+ may make it not affecting IPv4.
+ The rest of code is protocol independent,
+ and I do not like idea to uglify IPv4.
+
+ Actually, all the idea behind IPV6_PKTOPTIONS
+ looks not very well thought. For now we latch
+ options, received in the last packet, enqueued
+ by tcp. Feel free to propose better solution.
+ --ANK (980728)
+ */
+ if (np->rxopt.all)
+ opt_skb = skb_clone(skb, GFP_ATOMIC);
+
+ if (sk->sk_state == DCCP_OPEN) { /* Fast path */
+ if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len))
+ goto reset;
+ return 0;
+ }
+
+ if (sk->sk_state == DCCP_LISTEN) {
+ struct sock *nsk = dccp_v6_hnd_req(sk, skb);
+ if (!nsk)
+ goto discard;
+
+ /*
+ * Queue it on the new socket if the new socket is active,
+ * otherwise we just shortcircuit this and continue with
+ * the new socket..
+ */
+ if(nsk != sk) {
+ if (dccp_child_process(sk, nsk, skb))
+ goto reset;
+ if (opt_skb)
+ __kfree_skb(opt_skb);
+ return 0;
+ }
+ }
+
+ if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len))
+ goto reset;
+ return 0;
+
+reset:
+ dccp_v6_ctl_send_reset(skb);
+discard:
+ if (opt_skb)
+ __kfree_skb(opt_skb);
+ kfree_skb(skb);
+ return 0;
+}
+
+static int dccp_v6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
+{
+ const struct dccp_hdr *dh;
+ struct sk_buff *skb = *pskb;
+ struct sock *sk;
+ int rc;
+
+ /* Step 1: Check header basics: */
+
+ if (dccp_invalid_packet(skb))
+ goto discard_it;
+
+ dh = dccp_hdr(skb);
+
+ DCCP_SKB_CB(skb)->dccpd_seq = dccp_hdr_seq(skb);
+ DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type;
+
+ if (dccp_packet_without_ack(skb))
+ DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ;
+ else
+ DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb);
+
+ /* Step 2:
+ * Look up flow ID in table and get corresponding socket */
+ sk = __inet6_lookup(&dccp_hashinfo, &skb->nh.ipv6h->saddr,
+ dh->dccph_sport,
+ &skb->nh.ipv6h->daddr, ntohs(dh->dccph_dport),
+ inet6_iif(skb));
+ /*
+ * Step 2:
+ * If no socket ...
+ * Generate Reset(No Connection) unless P.type == Reset
+ * Drop packet and return
+ */
+ if (sk == NULL)
+ goto no_dccp_socket;
+
+ /*
+ * Step 2:
+ * ... or S.state == TIMEWAIT,
+ * Generate Reset(No Connection) unless P.type == Reset
+ * Drop packet and return
+ */
+
+ if (sk->sk_state == DCCP_TIME_WAIT)
+ goto do_time_wait;
+
+ if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
+ goto discard_and_relse;
+
+ if (sk_filter(sk, skb, 0))
+ goto discard_and_relse;
+
+ skb->dev = NULL;
+
+ bh_lock_sock(sk);
+ rc = 0;
+ if (!sock_owned_by_user(sk))
+ rc = dccp_v6_do_rcv(sk, skb);
+ else
+ sk_add_backlog(sk, skb);
+ bh_unlock_sock(sk);
+
+ sock_put(sk);
+ return rc ? -1 : 0;
+
+no_dccp_socket:
+ if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+ goto discard_it;
+ /*
+ * Step 2:
+ * Generate Reset(No Connection) unless P.type == Reset
+ * Drop packet and return
+ */
+ if (dh->dccph_type != DCCP_PKT_RESET) {
+ DCCP_SKB_CB(skb)->dccpd_reset_code =
+ DCCP_RESET_CODE_NO_CONNECTION;
+ dccp_v6_ctl_send_reset(skb);
+ }
+discard_it:
+
+ /*
+ * Discard frame
+ */
+
+ kfree_skb(skb);
+ return 0;
+
+discard_and_relse:
+ sock_put(sk);
+ goto discard_it;
+
+do_time_wait:
+ inet_twsk_put((struct inet_timewait_sock *)sk);
+ goto no_dccp_socket;
+}
+
+static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
+ .queue_xmit = inet6_csk_xmit,
+ .send_check = dccp_v6_send_check,
+ .rebuild_header = inet6_sk_rebuild_header,
+ .conn_request = dccp_v6_conn_request,
+ .syn_recv_sock = dccp_v6_request_recv_sock,
+ .net_header_len = sizeof(struct ipv6hdr),
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in6)
+};
+
+/*
+ * DCCP over IPv4 via INET6 API
+ */
+static struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
+ .queue_xmit = ip_queue_xmit,
+ .send_check = dccp_v4_send_check,
+ .rebuild_header = inet_sk_rebuild_header,
+ .conn_request = dccp_v6_conn_request,
+ .syn_recv_sock = dccp_v6_request_recv_sock,
+ .net_header_len = sizeof(struct iphdr),
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in6)
+};
+
+/* NOTE: A lot of things set to zero explicitly by call to
+ * sk_alloc() so need not be done here.
+ */
+static int dccp_v6_init_sock(struct sock *sk)
+{
+ int err = dccp_v4_init_sock(sk);
+
+ if (err == 0)
+ inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops;
+
+ return err;
+}
+
+static int dccp_v6_destroy_sock(struct sock *sk)
+{
+ dccp_v4_destroy_sock(sk);
+ return inet6_destroy_sock(sk);
+}
+
+static struct proto dccp_v6_prot = {
+ .name = "DCCPv6",
+ .owner = THIS_MODULE,
+ .close = dccp_close,
+ .connect = dccp_v6_connect,
+ .disconnect = dccp_disconnect,
+ .ioctl = dccp_ioctl,
+ .init = dccp_v6_init_sock,
+ .setsockopt = dccp_setsockopt,
+ .getsockopt = dccp_getsockopt,
+ .sendmsg = dccp_sendmsg,
+ .recvmsg = dccp_recvmsg,
+ .backlog_rcv = dccp_v6_do_rcv,
+ .hash = dccp_v6_hash,
+ .unhash = dccp_unhash,
+ .accept = inet_csk_accept,
+ .get_port = dccp_v6_get_port,
+ .shutdown = dccp_shutdown,
+ .destroy = dccp_v6_destroy_sock,
+ .orphan_count = &dccp_orphan_count,
+ .max_header = MAX_DCCP_HEADER,
+ .obj_size = sizeof(struct dccp6_sock),
+ .rsk_prot = &dccp6_request_sock_ops,
+ .twsk_obj_size = sizeof(struct dccp6_timewait_sock),
+};
+
+static struct inet6_protocol dccp_v6_protocol = {
+ .handler = dccp_v6_rcv,
+ .err_handler = dccp_v6_err,
+ .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,
+};
+
+static struct proto_ops inet6_dccp_ops = {
+ .family = PF_INET6,
+ .owner = THIS_MODULE,
+ .release = inet6_release,
+ .bind = inet6_bind,
+ .connect = inet_stream_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = inet6_getname,
+ .poll = dccp_poll,
+ .ioctl = inet6_ioctl,
+ .listen = inet_dccp_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct inet_protosw dccp_v6_protosw = {
+ .type = SOCK_DCCP,
+ .protocol = IPPROTO_DCCP,
+ .prot = &dccp_v6_prot,
+ .ops = &inet6_dccp_ops,
+ .capability = -1,
+};
+
+static int __init dccp_v6_init(void)
+{
+ int err = proto_register(&dccp_v6_prot, 1);
+
+ if (err != 0)
+ goto out;
+
+ err = inet6_add_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
+ if (err != 0)
+ goto out_unregister_proto;
+
+ inet6_register_protosw(&dccp_v6_protosw);
+out:
+ return err;
+out_unregister_proto:
+ proto_unregister(&dccp_v6_prot);
+ goto out;
+}
+
+static void __exit dccp_v6_exit(void)
+{
+ inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
+ inet6_unregister_protosw(&dccp_v6_protosw);
+ proto_unregister(&dccp_v6_prot);
+}
+
+module_init(dccp_v6_init);
+module_exit(dccp_v6_exit);
+
+/*
+ * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
+ * values directly, Also cover the case where the protocol is not specified,
+ * i.e. net-pf-PF_INET6-proto-0-type-SOCK_DCCP
+ */
+MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-33-type-6");
+MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-0-type-6");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>");
+MODULE_DESCRIPTION("DCCPv6 - Datagram Congestion Controlled Protocol");
diff --git a/net/dccp/ipv6.h b/net/dccp/ipv6.h
new file mode 100644
index 0000000..e4d4e93
--- /dev/null
+++ b/net/dccp/ipv6.h
@@ -0,0 +1,37 @@
+#ifndef _DCCP_IPV6_H
+#define _DCCP_IPV6_H
+/*
+ * net/dccp/ipv6.h
+ *
+ * An implementation of the DCCP protocol
+ * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/dccp.h>
+#include <linux/ipv6.h>
+
+struct dccp6_sock {
+ struct dccp_sock dccp;
+ /*
+ * ipv6_pinfo has to be the last member of dccp6_sock,
+ * see inet6_sk_generic.
+ */
+ struct ipv6_pinfo inet6;
+};
+
+struct dccp6_request_sock {
+ struct dccp_request_sock dccp;
+ struct inet6_request_sock inet6;
+};
+
+struct dccp6_timewait_sock {
+ struct inet_timewait_sock inet;
+ struct inet6_timewait_sock tw6;
+};
+
+#endif /* _DCCP_IPV6_H */
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 5c767b5..29261fc 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -52,7 +52,18 @@ void dccp_time_wait(struct sock *sk, int
if (tw != NULL) {
const struct inet_connection_sock *icsk = inet_csk(sk);
const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
-
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ if (tw->tw_family == PF_INET6) {
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ struct inet6_timewait_sock *tw6;
+
+ tw->tw_ipv6_offset = inet6_tw_offset(sk->sk_prot);
+ tw6 = inet6_twsk((struct sock *)tw);
+ ipv6_addr_copy(&tw6->tw_v6_daddr, &np->daddr);
+ ipv6_addr_copy(&tw6->tw_v6_rcv_saddr, &np->rcv_saddr);
+ tw->tw_ipv6only = np->ipv6only;
+ }
+#endif
/* Linkage updates. */
__inet_twsk_hashdance(tw, sk, &dccp_hashinfo);
^ permalink raw reply related
* Re: [PATCH 1/4][DCCP]: Introduce DCCPv6
From: Arnaldo Carvalho de Melo @ 2005-12-12 0:38 UTC (permalink / raw)
To: dccp
In-Reply-To: <39e6f6c70512110556r387a6a18q37baca095737e3e0@mail.gmail.com>
On 12/11/05, Francois Romieu <romieu@fr.zoreil.com> wrote:
> Arnaldo Carvalho de Melo <acme@ghostprotocols.net> :
> [...]
> index 0000000..23058dc
> --- /dev/null
> +++ b/net/dccp/ipv6.c
> [...]
> +static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
> +{
> [...]
> + skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
> + dccp_hdr_reset_len, GFP_ATOMIC);
> + if (skb = NULL)
> + return;
> [...]
> + /* sk = NULL, but it is safe for now. RST socket required. */
> + if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
> +
> + if ((xfrm_lookup(&skb->dst, &fl, NULL, 0)) < 0)
> + return;
>
> skb leak ?
>
> Same pattern in dccp_v6_ctl_send_ack/tcp_v6_send_{ack/reset}.
Good spot, chatted with Dave and will resend [1/4] with a fix for this
and 5/5 with the fix for the tcp_v6 ones, without resending [2/[2..4]],
thanks!
- Arnaldo
^ permalink raw reply
* Re: [PATCH 1/4][DCCP]: Introduce DCCPv6
From: Francois Romieu @ 2005-12-11 22:03 UTC (permalink / raw)
To: dccp
In-Reply-To: <39e6f6c70512110556r387a6a18q37baca095737e3e0@mail.gmail.com>
Arnaldo Carvalho de Melo <acme@ghostprotocols.net> :
[...]
index 0000000..23058dc
--- /dev/null
+++ b/net/dccp/ipv6.c
[...]
+static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
+{
[...]
+ skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_reset_len, GFP_ATOMIC);
+ if (skb = NULL)
+ return;
[...]
+ /* sk = NULL, but it is safe for now. RST socket required. */
+ if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
+
+ if ((xfrm_lookup(&skb->dst, &fl, NULL, 0)) < 0)
+ return;
skb leak ?
Same pattern in dccp_v6_ctl_send_ack/tcp_v6_send_{ack/reset}.
--
Ueimor
^ permalink raw reply
* [PATCH 4/4][INET]: Generalise tcp_v4_hash_connect
From: Arnaldo Carvalho de Melo @ 2005-12-11 13:57 UTC (permalink / raw)
To: dccp
[-- Attachment #1: Type: text/plain, Size: 144 bytes --]
Hi David,
Please consider pulling from:
master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
Best Regards,
- Arnaldo
[-- Attachment #2: 4.patch --]
[-- Type: text/x-patch, Size: 17669 bytes --]
tree d1285f31fef358cf1b30deb4bdc0723c006e64e5
parent 96ba4b224c010ad590ff1206f4f6d1cec1389ebf
author Arnaldo Carvalho de Melo <acme@mandriva.com> 1134308990 -0200
committer Arnaldo Carvalho de Melo <acme@mandriva.com> 1134308990 -0200
[INET]: Generalise tcp_v4_hash_connect
Renaming it to inet_hash_connect, making it possible to ditch
dccp_v4_hash_connect and share the same code with TCP instead.
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
------------------------------------------------------------------------------
drivers/char/random.c | 6 -
include/linux/random.h | 2
include/net/inet_hashtables.h | 3
net/dccp/ipv4.c | 160 -------------------------------------
net/ipv4/inet_hashtables.c | 178 ++++++++++++++++++++++++++++++++++++++++++
net/ipv4/tcp_ipv4.c | 173 ----------------------------------------
6 files changed, 186 insertions(+), 336 deletions(-)
------------------------------------------------------------------------------
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 7999da2..79b59d9 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1554,10 +1554,8 @@ __u32 secure_tcp_sequence_number(__u32 s
EXPORT_SYMBOL(secure_tcp_sequence_number);
-
-
-/* Generate secure starting point for ephemeral TCP port search */
-u32 secure_tcp_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport)
+/* Generate secure starting point for ephemeral IPV4 transport port search */
+u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport)
{
struct keydata *keyptr = get_keyptr();
u32 hash[4];
diff --git a/include/linux/random.h b/include/linux/random.h
index 7b2adb3..01424a8 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -52,7 +52,7 @@ extern void get_random_bytes(void *buf,
void generate_random_uuid(unsigned char uuid_out[16]);
extern __u32 secure_ip_id(__u32 daddr);
-extern u32 secure_tcp_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport);
+extern u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport);
extern u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr,
__u16 dport);
extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 07840ba..c83baa7 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -434,4 +434,7 @@ static inline struct sock *inet_lookup(s
return sk;
}
+
+extern int inet_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk);
#endif /* _INET_HASHTABLES_H */
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index f8c6a15..2702094 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -54,164 +54,6 @@ void dccp_unhash(struct sock *sk)
EXPORT_SYMBOL_GPL(dccp_unhash);
-/* called with local bh disabled */
-static int __dccp_v4_check_established(struct sock *sk, const __u16 lport,
- struct inet_timewait_sock **twp)
-{
- struct inet_sock *inet = inet_sk(sk);
- const u32 daddr = inet->rcv_saddr;
- const u32 saddr = inet->daddr;
- const int dif = sk->sk_bound_dev_if;
- INET_ADDR_COOKIE(acookie, saddr, daddr)
- const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
- unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash);
- const struct sock *sk2;
- const struct hlist_node *node;
- struct inet_timewait_sock *tw;
-
- prefetch(head->chain.first);
- write_lock(&head->lock);
-
- /* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) {
- tw = inet_twsk(sk2);
-
- if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
- goto not_unique;
- }
- tw = NULL;
-
- /* And established part... */
- sk_for_each(sk2, node, &head->chain) {
- if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
- goto not_unique;
- }
-
- /* Must record num and sport now. Otherwise we will see
- * in hash table socket with a funny identity. */
- inet->num = lport;
- inet->sport = htons(lport);
- sk->sk_hash = hash;
- BUG_TRAP(sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(&head->lock);
-
- if (twp != NULL) {
- *twp = tw;
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
- } else if (tw != NULL) {
- /* Silly. Should hash-dance instead... */
- inet_twsk_deschedule(tw, &dccp_death_row);
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
- inet_twsk_put(tw);
- }
-
- return 0;
-
-not_unique:
- write_unlock(&head->lock);
- return -EADDRNOTAVAIL;
-}
-
-/*
- * Bind a port for a connect operation and hash it.
- */
-static int dccp_v4_hash_connect(struct sock *sk)
-{
- const unsigned short snum = inet_sk(sk)->num;
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- int ret;
-
- if (snum == 0) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int remaining = (high - low) + 1;
- int rover = net_random() % (high - low) + low;
- struct hlist_node *node;
- struct inet_timewait_sock *tw = NULL;
-
- local_bh_disable();
- do {
- head = &dccp_hashinfo.bhash[inet_bhashfn(rover,
- dccp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
-
- /* Does not bother with rcv_saddr checks,
- * because the established check is already
- * unique enough.
- */
- inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->port == rover) {
- BUG_TRAP(!hlist_empty(&tb->owners));
- if (tb->fastreuse >= 0)
- goto next_port;
- if (!__dccp_v4_check_established(sk,
- rover,
- &tw))
- goto ok;
- goto next_port;
- }
- }
-
- tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep,
- head, rover);
- if (tb == NULL) {
- spin_unlock(&head->lock);
- break;
- }
- tb->fastreuse = -1;
- goto ok;
-
- next_port:
- spin_unlock(&head->lock);
- if (++rover > high)
- rover = low;
- } while (--remaining > 0);
-
- local_bh_enable();
-
- return -EADDRNOTAVAIL;
-
-ok:
- /* All locks still held and bhs disabled */
- inet_bind_hash(sk, tb, rover);
- if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(rover);
- __inet_hash(&dccp_hashinfo, sk, 0);
- }
- spin_unlock(&head->lock);
-
- if (tw != NULL) {
- inet_twsk_deschedule(tw, &dccp_death_row);
- inet_twsk_put(tw);
- }
-
- ret = 0;
- goto out;
- }
-
- head = &dccp_hashinfo.bhash[inet_bhashfn(snum,
- dccp_hashinfo.bhash_size)];
- tb = inet_csk(sk)->icsk_bind_hash;
- spin_lock_bh(&head->lock);
- if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
- __inet_hash(&dccp_hashinfo, sk, 0);
- spin_unlock_bh(&head->lock);
- return 0;
- } else {
- spin_unlock(&head->lock);
- /* No definite answer... Walk to established hash table */
- ret = __dccp_v4_check_established(sk, snum, NULL);
-out:
- local_bh_enable();
- return ret;
- }
-}
-
int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
@@ -272,7 +114,7 @@ int dccp_v4_connect(struct sock *sk, str
* complete initialization after this.
*/
dccp_set_state(sk, DCCP_REQUESTING);
- err = dccp_v4_hash_connect(sk);
+ err = inet_hash_connect(&dccp_death_row, sk);
if (err != 0)
goto failure;
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index e8d29fe..3322811 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -15,12 +15,14 @@
#include <linux/config.h>
#include <linux/module.h>
+#include <linux/random.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
+#include <net/ip.h>
/*
* Allocate and initialize a new local port bind bucket.
@@ -163,3 +165,179 @@ struct sock *__inet_lookup_listener(cons
}
EXPORT_SYMBOL_GPL(__inet_lookup_listener);
+
+/* called with local bh disabled */
+static int __inet_check_established(struct inet_timewait_death_row *death_row,
+ struct sock *sk, __u16 lport,
+ struct inet_timewait_sock **twp)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ struct inet_sock *inet = inet_sk(sk);
+ u32 daddr = inet->rcv_saddr;
+ u32 saddr = inet->daddr;
+ int dif = sk->sk_bound_dev_if;
+ INET_ADDR_COOKIE(acookie, saddr, daddr)
+ const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
+ unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
+ struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
+ struct sock *sk2;
+ const struct hlist_node *node;
+ struct inet_timewait_sock *tw;
+
+ prefetch(head->chain.first);
+ write_lock(&head->lock);
+
+ /* Check TIME-WAIT sockets first. */
+ sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) {
+ tw = inet_twsk(sk2);
+
+ if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
+ if (twsk_unique(sk, sk2, twp))
+ goto unique;
+ else
+ goto not_unique;
+ }
+ }
+ tw = NULL;
+
+ /* And established part... */
+ sk_for_each(sk2, node, &head->chain) {
+ if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
+ goto not_unique;
+ }
+
+unique:
+ /* Must record num and sport now. Otherwise we will see
+ * in hash table socket with a funny identity. */
+ inet->num = lport;
+ inet->sport = htons(lport);
+ sk->sk_hash = hash;
+ BUG_TRAP(sk_unhashed(sk));
+ __sk_add_node(sk, &head->chain);
+ sock_prot_inc_use(sk->sk_prot);
+ write_unlock(&head->lock);
+
+ if (twp) {
+ *twp = tw;
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+ } else if (tw) {
+ /* Silly. Should hash-dance instead... */
+ inet_twsk_deschedule(tw, death_row);
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+
+ inet_twsk_put(tw);
+ }
+
+ return 0;
+
+not_unique:
+ write_unlock(&head->lock);
+ return -EADDRNOTAVAIL;
+}
+
+static inline u32 inet_sk_port_offset(const struct sock *sk)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ return secure_ipv4_port_ephemeral(inet->rcv_saddr, inet->daddr,
+ inet->dport);
+}
+
+/*
+ * Bind a port for a connect operation and hash it.
+ */
+int inet_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ const unsigned short snum = inet_sk(sk)->num;
+ struct inet_bind_hashbucket *head;
+ struct inet_bind_bucket *tb;
+ int ret;
+
+ if (!snum) {
+ int low = sysctl_local_port_range[0];
+ int high = sysctl_local_port_range[1];
+ int range = high - low;
+ int i;
+ int port;
+ static u32 hint;
+ u32 offset = hint + inet_sk_port_offset(sk);
+ struct hlist_node *node;
+ struct inet_timewait_sock *tw = NULL;
+
+ local_bh_disable();
+ for (i = 1; i <= range; i++) {
+ port = low + (i + offset) % range;
+ head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
+ spin_lock(&head->lock);
+
+ /* Does not bother with rcv_saddr checks,
+ * because the established check is already
+ * unique enough.
+ */
+ inet_bind_bucket_for_each(tb, node, &head->chain) {
+ if (tb->port == port) {
+ BUG_TRAP(!hlist_empty(&tb->owners));
+ if (tb->fastreuse >= 0)
+ goto next_port;
+ if (!__inet_check_established(death_row,
+ sk, port,
+ &tw))
+ goto ok;
+ goto next_port;
+ }
+ }
+
+ tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, head, port);
+ if (!tb) {
+ spin_unlock(&head->lock);
+ break;
+ }
+ tb->fastreuse = -1;
+ goto ok;
+
+ next_port:
+ spin_unlock(&head->lock);
+ }
+ local_bh_enable();
+
+ return -EADDRNOTAVAIL;
+
+ok:
+ hint += i;
+
+ /* Head lock still held and bh's disabled */
+ inet_bind_hash(sk, tb, port);
+ if (sk_unhashed(sk)) {
+ inet_sk(sk)->sport = htons(port);
+ __inet_hash(hinfo, sk, 0);
+ }
+ spin_unlock(&head->lock);
+
+ if (tw) {
+ inet_twsk_deschedule(tw, death_row);;
+ inet_twsk_put(tw);
+ }
+
+ ret = 0;
+ goto out;
+ }
+
+ head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
+ tb = inet_csk(sk)->icsk_bind_hash;
+ spin_lock_bh(&head->lock);
+ if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
+ __inet_hash(hinfo, sk, 0);
+ spin_unlock_bh(&head->lock);
+ return 0;
+ } else {
+ spin_unlock(&head->lock);
+ /* No definite answer... Walk to established hash table */
+ ret = __inet_check_established(death_row, sk, snum, NULL);
+out:
+ local_bh_enable();
+ return ret;
+ }
+}
+
+EXPORT_SYMBOL_GPL(inet_hash_connect);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 6728772..c2fe61b 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -152,177 +152,6 @@ int tcp_twsk_unique(struct sock *sk, str
EXPORT_SYMBOL_GPL(tcp_twsk_unique);
-/* called with local bh disabled */
-static int __tcp_v4_check_established(struct sock *sk, __u16 lport,
- struct inet_timewait_sock **twp)
-{
- struct inet_sock *inet = inet_sk(sk);
- u32 daddr = inet->rcv_saddr;
- u32 saddr = inet->daddr;
- int dif = sk->sk_bound_dev_if;
- INET_ADDR_COOKIE(acookie, saddr, daddr)
- const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
- unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
- struct sock *sk2;
- const struct hlist_node *node;
- struct inet_timewait_sock *tw;
-
- prefetch(head->chain.first);
- write_lock(&head->lock);
-
- /* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
- tw = inet_twsk(sk2);
-
- if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
- if (twsk_unique(sk, sk2, twp))
- goto unique;
- else
- goto not_unique;
- }
- }
- tw = NULL;
-
- /* And established part... */
- sk_for_each(sk2, node, &head->chain) {
- if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
- goto not_unique;
- }
-
-unique:
- /* Must record num and sport now. Otherwise we will see
- * in hash table socket with a funny identity. */
- inet->num = lport;
- inet->sport = htons(lport);
- sk->sk_hash = hash;
- BUG_TRAP(sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(&head->lock);
-
- if (twp) {
- *twp = tw;
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
- } else if (tw) {
- /* Silly. Should hash-dance instead... */
- inet_twsk_deschedule(tw, &tcp_death_row);
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
- inet_twsk_put(tw);
- }
-
- return 0;
-
-not_unique:
- write_unlock(&head->lock);
- return -EADDRNOTAVAIL;
-}
-
-static inline u32 connect_port_offset(const struct sock *sk)
-{
- const struct inet_sock *inet = inet_sk(sk);
-
- return secure_tcp_port_ephemeral(inet->rcv_saddr, inet->daddr,
- inet->dport);
-}
-
-/*
- * Bind a port for a connect operation and hash it.
- */
-static inline int tcp_v4_hash_connect(struct sock *sk)
-{
- const unsigned short snum = inet_sk(sk)->num;
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- int ret;
-
- if (!snum) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int range = high - low;
- int i;
- int port;
- static u32 hint;
- u32 offset = hint + connect_port_offset(sk);
- struct hlist_node *node;
- struct inet_timewait_sock *tw = NULL;
-
- local_bh_disable();
- for (i = 1; i <= range; i++) {
- port = low + (i + offset) % range;
- head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
-
- /* Does not bother with rcv_saddr checks,
- * because the established check is already
- * unique enough.
- */
- inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->port == port) {
- BUG_TRAP(!hlist_empty(&tb->owners));
- if (tb->fastreuse >= 0)
- goto next_port;
- if (!__tcp_v4_check_established(sk,
- port,
- &tw))
- goto ok;
- goto next_port;
- }
- }
-
- tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
- if (!tb) {
- spin_unlock(&head->lock);
- break;
- }
- tb->fastreuse = -1;
- goto ok;
-
- next_port:
- spin_unlock(&head->lock);
- }
- local_bh_enable();
-
- return -EADDRNOTAVAIL;
-
-ok:
- hint += i;
-
- /* Head lock still held and bh's disabled */
- inet_bind_hash(sk, tb, port);
- if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(port);
- __inet_hash(&tcp_hashinfo, sk, 0);
- }
- spin_unlock(&head->lock);
-
- if (tw) {
- inet_twsk_deschedule(tw, &tcp_death_row);;
- inet_twsk_put(tw);
- }
-
- ret = 0;
- goto out;
- }
-
- head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
- tb = inet_csk(sk)->icsk_bind_hash;
- spin_lock_bh(&head->lock);
- if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
- __inet_hash(&tcp_hashinfo, sk, 0);
- spin_unlock_bh(&head->lock);
- return 0;
- } else {
- spin_unlock(&head->lock);
- /* No definite answer... Walk to established hash table */
- ret = __tcp_v4_check_established(sk, snum, NULL);
-out:
- local_bh_enable();
- return ret;
- }
-}
-
/* This will initiate an outgoing connection. */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
@@ -403,7 +232,7 @@ int tcp_v4_connect(struct sock *sk, stru
* complete initialization after this.
*/
tcp_set_state(sk, TCP_SYN_SENT);
- err = tcp_v4_hash_connect(sk);
+ err = inet_hash_connect(&tcp_death_row, sk);
if (err)
goto failure;
^ permalink raw reply related
* [PATCH 3/4][TWSK]: Introduce struct timewait_sock_ops
From: Arnaldo Carvalho de Melo @ 2005-12-11 13:57 UTC (permalink / raw)
To: dccp
[-- Attachment #1: Type: text/plain, Size: 144 bytes --]
Hi David,
Please consider pulling from:
master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6.16.git
Best Regards,
- Arnaldo
[-- Attachment #2: 3.patch --]
[-- Type: text/x-patch, Size: 13935 bytes --]
tree a2bd0ce30e3dd56e9605fe508af4dc18eac9445f
parent 384beca5393a67bab83c2825fe354791b6a27052
author Arnaldo Carvalho de Melo <acme@mandriva.com> 1134307585 -0200
committer Arnaldo Carvalho de Melo <acme@mandriva.com> 1134307585 -0200
[TWSK]: Introduce struct timewait_sock_ops
So that we can share several timewait sockets related functions and make the
timewait mini sockets infrastructure closer to the request mini sockets one.
Next changesets will take advantage of this, moving more code out of TCP and
DCCP v4 and v6 to common infrastructure.
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
------------------------------------------------------------------------------
include/linux/ipv6.h | 3 +
include/net/inet_timewait_sock.h | 3 +
include/net/sock.h | 4 +-
include/net/tcp.h | 3 +
include/net/timewait_sock.h | 31 +++++++++++++++++
net/core/sock.c | 21 ++++++-----
net/dccp/ipv4.c | 9 ++++
net/dccp/ipv6.c | 6 ++-
net/ipv4/inet_timewait_sock.c | 5 +-
net/ipv4/tcp_ipv4.c | 71 +++++++++++++++++++++++----------------
net/ipv6/tcp_ipv6.c | 25 ++++---------
11 files changed, 118 insertions(+), 63 deletions(-)
------------------------------------------------------------------------------
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 7d39085..a0d0489 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -360,7 +360,8 @@ struct tcp6_timewait_sock {
static inline u16 inet6_tw_offset(const struct proto *prot)
{
- return prot->twsk_obj_size - sizeof(struct inet6_timewait_sock);
+ return prot->twsk_prot->twsk_obj_size -
+ sizeof(struct inet6_timewait_sock);
}
static inline struct inet6_timewait_sock *inet6_twsk(const struct sock *sk)
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index ca240f8..e396a65 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -26,6 +26,7 @@
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <net/timewait_sock.h>
#include <asm/atomic.h>
@@ -200,7 +201,7 @@ static inline void inet_twsk_put(struct
printk(KERN_DEBUG "%s timewait_sock %p released\n",
tw->tw_prot->name, tw);
#endif
- kmem_cache_free(tw->tw_prot->twsk_slab, tw);
+ kmem_cache_free(tw->tw_prot->twsk_prot->twsk_slab, tw);
module_put(owner);
}
}
diff --git a/include/net/sock.h b/include/net/sock.h
index 6036340..d7910e7 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -493,6 +493,7 @@ extern void sk_stream_kill_queues(struct
extern int sk_wait_data(struct sock *sk, long *timeo);
struct request_sock_ops;
+struct timewait_sock_ops;
/* Networking protocol blocks we attach to sockets.
* socket layer -> transport layer interface
@@ -557,11 +558,10 @@ struct proto {
kmem_cache_t *slab;
unsigned int obj_size;
- kmem_cache_t *twsk_slab;
- unsigned int twsk_obj_size;
atomic_t *orphan_count;
struct request_sock_ops *rsk_prot;
+ struct timewait_sock_ops *twsk_prot;
struct module *owner;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 83b117a..176221c 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -287,6 +287,9 @@ extern int tcp_rcv_established(struct
extern void tcp_rcv_space_adjust(struct sock *sk);
+extern int tcp_twsk_unique(struct sock *sk,
+ struct sock *sktw, void *twp);
+
static inline void tcp_dec_quickack_mode(struct sock *sk,
const unsigned int pkts)
{
diff --git a/include/net/timewait_sock.h b/include/net/timewait_sock.h
new file mode 100644
index 0000000..2544281
--- /dev/null
+++ b/include/net/timewait_sock.h
@@ -0,0 +1,31 @@
+/*
+ * NET Generic infrastructure for Network protocols.
+ *
+ * Authors: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _TIMEWAIT_SOCK_H
+#define _TIMEWAIT_SOCK_H
+
+#include <linux/slab.h>
+#include <net/sock.h>
+
+struct timewait_sock_ops {
+ kmem_cache_t *twsk_slab;
+ unsigned int twsk_obj_size;
+ int (*twsk_unique)(struct sock *sk,
+ struct sock *sktw, void *twp);
+};
+
+static inline int twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
+{
+ if (sk->sk_prot->twsk_prot->twsk_unique != NULL)
+ return sk->sk_prot->twsk_prot->twsk_unique(sk, sktw, twp);
+ return 0;
+}
+
+#endif /* _TIMEWAIT_SOCK_H */
diff --git a/net/core/sock.c b/net/core/sock.c
index 13cc3be..6465b0e 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1488,7 +1488,7 @@ int proto_register(struct proto *prot, i
}
}
- if (prot->twsk_obj_size) {
+ if (prot->twsk_prot != NULL) {
static const char mask[] = "tw_sock_%s";
timewait_sock_slab_name = kmalloc(strlen(prot->name) + sizeof(mask) - 1, GFP_KERNEL);
@@ -1497,11 +1497,12 @@ int proto_register(struct proto *prot, i
goto out_free_request_sock_slab;
sprintf(timewait_sock_slab_name, mask, prot->name);
- prot->twsk_slab = kmem_cache_create(timewait_sock_slab_name,
- prot->twsk_obj_size,
- 0, SLAB_HWCACHE_ALIGN,
- NULL, NULL);
- if (prot->twsk_slab == NULL)
+ prot->twsk_prot->twsk_slab =
+ kmem_cache_create(timewait_sock_slab_name,
+ prot->twsk_prot->twsk_obj_size,
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (prot->twsk_prot->twsk_slab == NULL)
goto out_free_timewait_sock_slab_name;
}
}
@@ -1548,12 +1549,12 @@ void proto_unregister(struct proto *prot
prot->rsk_prot->slab = NULL;
}
- if (prot->twsk_slab != NULL) {
- const char *name = kmem_cache_name(prot->twsk_slab);
+ if (prot->twsk_prot != NULL && prot->twsk_prot->twsk_slab != NULL) {
+ const char *name = kmem_cache_name(prot->twsk_prot->twsk_slab);
- kmem_cache_destroy(prot->twsk_slab);
+ kmem_cache_destroy(prot->twsk_prot->twsk_slab);
kfree(name);
- prot->twsk_slab = NULL;
+ prot->twsk_prot->twsk_slab = NULL;
}
}
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index acdd96c..f8c6a15 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -20,6 +20,7 @@
#include <net/icmp.h>
#include <net/inet_hashtables.h>
#include <net/sock.h>
+#include <net/timewait_sock.h>
#include <net/tcp_states.h>
#include <net/xfrm.h>
@@ -1309,6 +1310,10 @@ static struct request_sock_ops dccp_requ
.send_reset = dccp_v4_ctl_send_reset,
};
+static struct timewait_sock_ops dccp_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct inet_timewait_sock),
+};
+
struct proto dccp_prot = {
.name = "DCCP",
.owner = THIS_MODULE,
@@ -1332,5 +1337,7 @@ struct proto dccp_prot = {
.max_header = MAX_DCCP_HEADER,
.obj_size = sizeof(struct dccp_sock),
.rsk_prot = &dccp_request_sock_ops,
- .twsk_obj_size = sizeof(struct inet_timewait_sock),
+ .twsk_prot = &dccp_timewait_sock_ops,
};
+
+EXPORT_SYMBOL_GPL(dccp_prot);
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 23058dc..34e7f64 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -652,6 +652,10 @@ static struct request_sock_ops dccp6_req
.send_reset = dccp_v6_ctl_send_reset,
};
+static struct timewait_sock_ops dccp6_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct dccp6_timewait_sock),
+};
+
static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
{
struct ipv6_pinfo *np = inet6_sk(sk);
@@ -1361,7 +1365,7 @@ static struct proto dccp_v6_prot = {
.max_header = MAX_DCCP_HEADER,
.obj_size = sizeof(struct dccp6_sock),
.rsk_prot = &dccp6_request_sock_ops,
- .twsk_obj_size = sizeof(struct dccp6_timewait_sock),
+ .twsk_prot = &dccp6_timewait_sock_ops,
};
static struct inet6_protocol dccp_v6_protocol = {
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index a010e9a..417f126 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -90,8 +90,9 @@ EXPORT_SYMBOL_GPL(__inet_twsk_hashdance)
struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state)
{
- struct inet_timewait_sock *tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_slab,
- SLAB_ATOMIC);
+ struct inet_timewait_sock *tw =
+ kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
+ SLAB_ATOMIC);
if (tw != NULL) {
const struct inet_sock *inet = inet_sk(sk);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 0b5ab04..6728772 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -69,6 +69,7 @@
#include <net/transp_v6.h>
#include <net/ipv6.h>
#include <net/inet_common.h>
+#include <net/timewait_sock.h>
#include <net/xfrm.h>
#include <linux/inet.h>
@@ -118,6 +119,39 @@ static inline __u32 tcp_v4_init_sequence
skb->h.th->source);
}
+int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
+{
+ const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ /* With PAWS, it is safe from the viewpoint
+ of data integrity. Even without PAWS it is safe provided sequence
+ spaces do not overlap i.e. at data rates <= 80Mbit/sec.
+
+ Actually, the idea is close to VJ's one, only timestamp cache is
+ held not per host, but per port pair and TW bucket is used as state
+ holder.
+
+ If TW bucket has been already destroyed we fall back to VJ's scheme
+ and use initial timestamp retrieved from peer table.
+ */
+ if (tcptw->tw_ts_recent_stamp &&
+ (twp == NULL || (sysctl_tcp_tw_reuse &&
+ xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
+ tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
+ if (tp->write_seq == 0)
+ tp->write_seq = 1;
+ tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
+ tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
+ sock_hold(sktw);
+ return 1;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(tcp_twsk_unique);
+
/* called with local bh disabled */
static int __tcp_v4_check_established(struct sock *sk, __u16 lport,
struct inet_timewait_sock **twp)
@@ -142,35 +176,9 @@ static int __tcp_v4_check_established(st
tw = inet_twsk(sk2);
if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
- const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2);
- struct tcp_sock *tp = tcp_sk(sk);
-
- /* With PAWS, it is safe from the viewpoint
- of data integrity. Even without PAWS it
- is safe provided sequence spaces do not
- overlap i.e. at data rates <= 80Mbit/sec.
-
- Actually, the idea is close to VJ's one,
- only timestamp cache is held not per host,
- but per port pair and TW bucket is used
- as state holder.
-
- If TW bucket has been already destroyed we
- fall back to VJ's scheme and use initial
- timestamp retrieved from peer table.
- */
- if (tcptw->tw_ts_recent_stamp &&
- (!twp || (sysctl_tcp_tw_reuse &&
- xtime.tv_sec -
- tcptw->tw_ts_recent_stamp > 1))) {
- tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
- if (tp->write_seq == 0)
- tp->write_seq = 1;
- tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
- tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
- sock_hold(sk2);
+ if (twsk_unique(sk, sk2, twp))
goto unique;
- } else
+ else
goto not_unique;
}
}
@@ -869,6 +877,11 @@ struct request_sock_ops tcp_request_sock
.send_reset = tcp_v4_send_reset,
};
+static struct timewait_sock_ops tcp_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct tcp_timewait_sock),
+ .twsk_unique = tcp_twsk_unique,
+};
+
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
struct inet_request_sock *ireq;
@@ -1979,7 +1992,7 @@ struct proto tcp_prot = {
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp_sock),
- .twsk_obj_size = sizeof(struct tcp_timewait_sock),
+ .twsk_prot = &tcp_timewait_sock_ops,
.rsk_prot = &tcp_request_sock_ops,
};
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 4c4983c..7a077de 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -60,6 +60,7 @@
#include <net/addrconf.h>
#include <net/snmp.h>
#include <net/dsfield.h>
+#include <net/timewait_sock.h>
#include <asm/uaccess.h>
@@ -147,22 +148,9 @@ static int __tcp_v6_check_established(st
ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
- const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2);
- struct tcp_sock *tp = tcp_sk(sk);
-
- if (tcptw->tw_ts_recent_stamp &&
- (!twp ||
- (sysctl_tcp_tw_reuse &&
- xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
- /* See comment in tcp_ipv4.c */
- tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
- if (!tp->write_seq)
- tp->write_seq = 1;
- tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
- tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
- sock_hold(sk2);
+ if (twsk_unique(sk, sk2, twp))
goto unique;
- } else
+ else
goto not_unique;
}
}
@@ -711,6 +699,11 @@ static struct request_sock_ops tcp6_requ
.send_reset = tcp_v6_send_reset
};
+static struct timewait_sock_ops tcp6_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct tcp6_timewait_sock),
+ .twsk_unique = tcp_twsk_unique,
+};
+
static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
{
struct ipv6_pinfo *np = inet6_sk(sk);
@@ -1753,7 +1746,7 @@ struct proto tcpv6_prot = {
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp6_sock),
- .twsk_obj_size = sizeof(struct tcp6_timewait_sock),
+ .twsk_prot = &tcp6_timewait_sock_ops,
.rsk_prot = &tcp6_request_sock_ops,
};
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox