Sie sind auf Seite 1von 3

The previous discussion about TCP in Section focused primarily on how the TCP

protocol was interfaced to the socket, and how data was removed from the
application and placed in queues for transmission. In this section, we cover the
actual packet transmission (see Figure).

TCP transmit sequence.

TCP transmit sequence.

Transmit the TCP Segments, the Tcp_transmit_Skb Function

Now it is time to transmit the TCP segments, and the function tcp_Transmit_skb does
the actual packet transmission. It sends the packets that are queued to the socket.
It can be called from anywhere in TCP state processing when there is a request to
send a segment. Earlier, we saw how tcp_sendmsg readied the segments for
transmission and queued them to the socket�s write queue. In tcp_transmit_skb, we
build the TCP packet header and pass the packet on to IP.

int tcp_transmit_skb(struct sock *sk,struct sk_buff *skb) {


If we receive a NULL socket buffer, we do nothing.

if(skb != NULL) {
Inet points to the inet options structure, and tp points to the TCP options
structure. It is in tcp_opt where the socket keeps most of the configuration and
connection state information for TCP. Tcb points to the TCP control buffer
containing most of the flags as well as the partially constructed TCP header. Th is
a pointer to the TCP header. Later, it will point to the header part of the skb,
and sysctl_flags is for some critical parameters configured via sysctl and
setsockopt calls.

struct inet_opt *inet = inet_sk(sk);


struct tcp_opt *tp = inet_sk(sk);
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
int tcp_header_size = tp->tcp_header_len;
struct tcphdr *th;
int sysctl_flags;
int err;
#define SYSCTL_FLAG_TSTAMPS0x1
#define SYSCTL_FLAG_WSCALE0x2
#define SYSCTL_FLAG_SACK0x4
sysctl_flags = 0;
?
Here, we check to see if this outgoing packet is a SYN packet, and if so, we check
for the presence of certain TCP options in the flags field of the control buffer
structure. This is because the TCP header length may need to be extended to account
for certain TCP options, which generally include timestamps, window scaling, and
Selective Acknowledgement (SACK) [RFC 2018].

if (tcb->flags & TCPCB_FLAG_SYN) {


tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
if(sysctl_tcp_timestamps) {
tcp_header_size += TCPOLEN_TSTAMP_ALIGNED;
sysctl_flags |= SYSCTL_FLAG_TSTAMPS;
}
if(sysctl_tcp_window_scaling) {
tcp_header_size += TCPOLEN_WSCALE_ALIGNED;
sysctl_flags |= SYSCTL_FLAG_WSCALE;
}
if(sysctl_tcp_sack) {
sysctl_flags |= SYSCTL_FLAG_SACK;
if(!(sysctl_flags & SYSCTL_FLAG_TSTAMPS))
tcp_header_size += TCPOLEN_SACKPERM_ALIGNED;
}
} else if (tp->eff_sacks) {
?
The following processing is for the SACK option. If we are sending SACKs in this
segment, we increment the header to account for the number of SACK blocks that are
being sent along with this packet. The header length is adjusted by eight for each
SACK block.

tcp_header_size += (TCPOLEN_SACK_BASE_ALIGNED +
(tp->eff_sacks * TCPOLEN_SACK_PERBLOCK));
}
?
Now that we know the size of the TCP header, we adjust the skb to allow for
sufficient space.

th = (struct tcphdr *) skb_push(skb, tcp_header_size);


skb->h.th = th;
skb_set_owner_w(skb, sk);
?
At this point, the TCP header is built, space for the checksum field is reserved,
and the header size is calculated. Some of the header fields used to build the
header are in inet_opt structure, some are in the TCP control buffer, and some are
in the TCP options structure.

th->source = inet->sport;
th->dest = inet->dport;
th->seq = htonl(tcb->seq);
th->ack_seq = htonl(tp->rcv_nxt);
*(((__u16 *)th)+6)=htons(((tcp_header_size >> 2)<<12)|
tcb->flags);
?
The advertised window size is determined. If this packet is a SYN packet;
otherwise, the window size is scaled by calling tcp_select_window.

if (tcb->flags & TCPCB_FLAG_SYN) {


th->window = htons(tp->rcv_wnd);
} else {
th->window = htons(tcp_select_window(sk));
}
?
If urgent mode is set, we calculate the urgent pointer and set the URG flag in the
TCP header.

if (tp->urg_mode && between(tp->snd_up, tcb->seq+1,


tcb->seq+0xFFFF)) {
th->urg_ptr = htons(tp->snd_up-tcb->seq);
th->urg = 1;
}
?
Here we actually build the TCP options part of the packet header. If this is a SYN
segment, we include the window scale option. If not, it is left out. We check to
see if we have a timestamp, window scaling or SACK option from the sysctl_flags set
earlier. We call tcp_syn_build_options to build the options with window scaling,
and we call tcp_build_and_update_options is for non-SYN packets and do not include
window scaling.
if (tcb->flags & TCPCB_FLAG_SYN) {
tcp_syn_build_options((__u32 *)(th + 1),
tcp_advertise_mss(sk),
(sysctl_flags & SYSCTL_FLAG_TSTAMPS),
(sysctl_flags & SYSCTL_FLAG_SACK),
(sysctl_flags & SYSCTL_FLAG_WSCALE),
tp->rcv_wscale,
tcb->when,
tp->ts_recent);
} else {
tcp_build_and_update_options((__u32 *)(th + 1),
tp, tcb->when);
?
We call the TCP_ECN_send to send explicit congestion notification. This TCP
modification [RFC3168] changes the TCP header to make it slightly incompatible with
the header specified by RFC 793.

TCP_ECN_send(sk, tp, skb, tcp_header_size); }


We calculate the checksum. We check the flags to see if we are sending an ACK or
sending data and we update the delayed acknowledgment status depending on whether
we are sending data or an ACK. we are sending data, we update the congestion window
and mark the send timestamp. In addition, we increment the counter to indicate the
number of TCP segments that have been sent.

tp->af_specific->send_check(sk, th,skb->len, skb);


if (tcb->flags & TCPCB_FLAG_ACK)
tcp_event_ack_sent(sk);
if (skb->len != tcp_header_size)
tcp_event_data_sent(tp, skb);
TCP_INC_STATS(TcpOutSegs);
?
Now we call the actual transmission function to send this segment. The segment will
be queued up for processing by the next stage whether the packet has an internal or
an external destination.

err = tp->af_specific->queue_xmit(skb);
if (err <= 0)
return err;
tcp_enter_cwr(tp);
?
A return of a value less than zero tells the caller that the packet is dropped. We
return all errors except explicit congestion notification, which doesn�t indicate
to the caller that the packet was dropped.

return err == NET_XMIT_CN ? 0 : err;


}
#undef SYSCTL_FLAG_TSTAMPS
#undef SYSCTL_FLAG_WSCALE
#undef SYSCTL_FLAG_SACK
return -ENOBUFS;
}
?

Das könnte Ihnen auch gefallen