Skip to content

Commit 19f5dec

Browse files
Fix bugs in TCP checksumming
Wasn't computing pseudo-header checksums (need to use tcp_v*_check). Also, wasn't even trying to compute checksums for control packets.
1 parent caffa25 commit 19f5dec

9 files changed

Lines changed: 219 additions & 48 deletions

File tree

homa_devel.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,6 @@ void homa_freeze_peers(void)
424424
freeze.common.type = FREEZE;
425425
freeze.common.sport = htons(hsk->port);
426426
freeze.common.dport = 0;
427-
IF_NO_STRIP(homa_set_hijack(&freeze.common));
428427
freeze.common.sender_id = 0;
429428

430429
rhashtable_walk_enter(&hnet->homa->peertab->ht, &iter);
@@ -1270,3 +1269,33 @@ void homa_validate_rbtree(struct rb_node *node, int depth, char *message)
12701269
#endif /* __UNIT_TEST__ */
12711270
}
12721271
#endif /* See strip.py */
1272+
1273+
/**
1274+
* homa_tcp_checksum() - Compute the TCP checksum for a packet. This is
1275+
* done "from scratch", i.e. not using any existing information such
1276+
* as skb->csum.
1277+
* @skb: Contains the packet to checksum
1278+
* Return: Checksum for the packet: 0 is the "correct" value.
1279+
*/
1280+
int homa_tcp_checksum(struct sk_buff *skb)
1281+
{
1282+
int tcp_len = skb->len - skb_transport_offset(skb);
1283+
__wsum data_csum;
1284+
1285+
// Calculate the sum of the TCP header + data manually
1286+
data_csum = skb_checksum(skb, skb_transport_offset(skb), tcp_len, 0);
1287+
1288+
if (skb_is_ipv6(skb)) {
1289+
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
1290+
1291+
// Fold the manual sum with the IPv6 pseudo-header
1292+
return csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, tcp_len,
1293+
IPPROTO_TCP, data_csum);
1294+
} else {
1295+
const struct iphdr *iph = ip_hdr(skb);
1296+
1297+
// Fold the manual sum with the IPv4 pseudo-header
1298+
return csum_tcpudp_magic(iph->saddr, iph->daddr, tcp_len,
1299+
IPPROTO_TCP, data_csum);
1300+
}
1301+
}

homa_devel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ int homa_snprintf(char *buffer, int size, int used,
134134
const char *format, ...) __printf(4, 5);
135135
char *homa_symbol_for_type(uint8_t type);
136136
char *homa_symbol_for_state(struct homa_rpc *rpc);
137+
int homa_tcp_checksum(struct sk_buff *skb);
137138
int homa_validate_incoming(struct homa *homa, int verbose,
138139
int *link_errors);
139140

homa_impl.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -572,19 +572,16 @@ static inline struct homa_skb_info *homa_get_skb_info(struct sk_buff *skb)
572572
}
573573

574574
/**
575-
* homa_set_doff() - Fills in the doff TCP header field for a Homa packet.
576-
* @h: Packet header whose doff field is to be set.
575+
* homa_set_doff() - Fills in the doff TCP header field for a packet.
576+
* @skb: Packet whose doff field is to be set.
577577
* @size: Size of the "header", bytes (must be a multiple of 4). This
578578
* information is used only for TSO; it's the number of bytes
579579
* that should be replicated in each segment. The bytes after
580580
* this will be distributed among segments.
581581
*/
582-
static inline void homa_set_doff(struct homa_data_hdr *h, int size)
582+
static inline void homa_set_doff(struct sk_buff *skb, int size)
583583
{
584-
/* This fills in the high-order 4 bits of doff with the number of
585-
* words in the neader.
586-
*/
587-
h->common.doff = size << 2;
584+
tcp_hdr(skb)->doff = size >> 2;
588585
}
589586

590587
/** skb_is_ipv6() - Return true if the packet is encapsulated with IPv6,

homa_incoming.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,6 @@ void homa_need_ack_pkt(struct sk_buff *skb, struct homa_sock *hsk,
997997
ack.common.type = ACK;
998998
ack.common.sport = h->dport;
999999
ack.common.dport = h->sport;
1000-
IF_NO_STRIP(homa_set_hijack(&ack.common));
10011000
ack.common.sender_id = cpu_to_be64(id);
10021001
ack.num_acks = htons(homa_peer_get_acks(peer,
10031002
HOMA_MAX_ACKS_PER_PKT,

homa_outgoing.c

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,39 @@
1717
#include "homa_stub.h"
1818
#endif /* See strip.py */
1919

20+
#ifndef __STRIP__ /* See strip.py */
21+
/**
22+
* homa_set_hijack() - Set fields in an outgoing Homa packet that are needed
23+
* for TCP hijacking to work properly. This function doesn't actually cause
24+
* the packet to be sent via TCP (that is determined by hsk->sock.sk_protocol,
25+
* which is set elsewhere). The modifications made here are safe even if the
26+
* packet isn't actually sent via TCP.
27+
* @skb: Packet buffer in which to set fields.
28+
* @peer: Peer that contains source and destination addresses for the packet.
29+
* @ipv6: True means the packet is going to be sent via IPv6; false means
30+
* IPv4.
31+
*/
32+
static inline void homa_set_hijack(struct sk_buff *skb, struct homa_peer *peer,
33+
bool ipv6)
34+
{
35+
struct homa_common_hdr *h;
36+
37+
h = (struct homa_common_hdr *)skb_transport_header(skb);
38+
h->flags = HOMA_TCP_FLAGS;
39+
h->urgent = htons(HOMA_TCP_URGENT);
40+
/* Arrange for proper TCP checksumming. */
41+
skb->ip_summed = CHECKSUM_PARTIAL;
42+
skb->csum_start = skb_transport_header(skb) - skb->head;
43+
skb->csum_offset = offsetof(struct homa_common_hdr, checksum);
44+
if (ipv6)
45+
h->checksum = ~tcp_v6_check(skb->len, &peer->flow.u.ip6.saddr,
46+
&peer->flow.u.ip6.daddr, 0);
47+
else
48+
h->checksum = ~tcp_v4_check(skb->len, peer->flow.u.ip4.saddr,
49+
peer->flow.u.ip4.daddr, 0);
50+
}
51+
#endif /* See strip.py */
52+
2053
/**
2154
* homa_message_out_init() - Initialize rpc->msgout.
2255
* @rpc: RPC whose output message should be initialized. Must be
@@ -160,8 +193,7 @@ struct sk_buff *homa_tx_data_pkt_alloc(struct homa_rpc *rpc,
160193
h->common.dport = htons(rpc->dport);
161194
h->common.sequence = htonl(offset);
162195
h->common.type = DATA;
163-
IF_NO_STRIP(homa_set_hijack(&h->common));
164-
homa_set_doff(h, sizeof(struct homa_data_hdr));
196+
homa_set_doff(skb, sizeof(struct homa_data_hdr));
165197
h->common.checksum = 0;
166198
h->common.sender_id = cpu_to_be64(rpc->id);
167199
h->message_length = htonl(rpc->msgout.length);
@@ -190,7 +222,7 @@ struct sk_buff *homa_tx_data_pkt_alloc(struct homa_rpc *rpc,
190222
#else /* See strip.py */
191223
if (segs > 1) {
192224
#endif /* See strip.py */
193-
homa_set_doff(h, sizeof(struct homa_data_hdr) -
225+
homa_set_doff(skb, sizeof(struct homa_data_hdr) -
194226
sizeof(struct homa_seg_hdr));
195227
#ifndef __STRIP__ /* See strip.py */
196228
h->seg.offset = htonl(offset);
@@ -429,7 +461,6 @@ int homa_xmit_control(enum homa_packet_type type, void *contents,
429461
h->type = type;
430462
h->sport = htons(rpc->hsk->port);
431463
h->dport = htons(rpc->dport);
432-
IF_NO_STRIP(homa_set_hijack(h));
433464
h->sender_id = cpu_to_be64(rpc->id);
434465
return __homa_xmit_control(contents, length, rpc->peer, rpc->hsk);
435466
}
@@ -474,12 +505,16 @@ int __homa_xmit_control(void *contents, size_t length, struct homa_peer *peer,
474505
priority = hsk->homa->num_priorities - 1;
475506
#endif /* See strip.py */
476507
skb->ooo_okay = 1;
508+
homa_set_doff(skb, length);
477509
#ifndef __STRIP__ /* See strip.py */
478510
if (hsk->inet.sk.sk_family == AF_INET6) {
511+
homa_set_hijack(skb, peer, true);
479512
result = ip6_xmit(&hsk->inet.sk, skb, &peer->flow.u.ip6, 0,
480513
NULL, hsk->homa->priority_map[priority] << 5,
481514
0);
482515
} else {
516+
homa_set_hijack(skb, peer, false);
517+
483518
/* This will find its way to the DSCP field in the IPv4 hdr. */
484519
hsk->inet.tos = hsk->homa->priority_map[priority] << 5;
485520
result = ip_queue_xmit(&hsk->inet.sk, skb, &peer->flow);
@@ -535,7 +570,6 @@ void homa_xmit_unknown(struct sk_buff *skb, struct homa_sock *hsk)
535570
unknown.common.sport = h->dport;
536571
unknown.common.dport = h->sport;
537572
unknown.common.type = RPC_UNKNOWN;
538-
IF_NO_STRIP(homa_set_hijack(&unknown.common));
539573
unknown.common.sender_id = cpu_to_be64(homa_local_id(h->sender_id));
540574
peer = homa_peer_get(hsk, &saddr);
541575
if (!IS_ERR(peer)) {
@@ -680,15 +714,13 @@ void __homa_xmit_data(struct sk_buff *skb, struct homa_rpc *rpc)
680714
skb_dst_set(skb, homa_get_dst(rpc->peer, rpc->hsk));
681715

682716
skb->ooo_okay = 1;
683-
skb->ip_summed = CHECKSUM_PARTIAL;
684-
skb->csum_start = skb_transport_header(skb) - skb->head;
685-
skb->csum_offset = offsetof(struct homa_common_hdr, checksum);
686717
if (rpc->hsk->inet.sk.sk_family == AF_INET6) {
687718
tt_record4("calling ip6_xmit: wire_bytes %d, peer 0x%x, id %d, offset %d",
688719
homa_get_skb_info(skb)->wire_bytes,
689720
tt_addr(rpc->peer->addr), rpc->id,
690721
homa_get_skb_info(skb)->offset);
691722
#ifndef __STRIP__ /* See strip.py */
723+
homa_set_hijack(skb, rpc->peer, true);
692724
err = ip6_xmit(&rpc->hsk->inet.sk, skb, &rpc->peer->flow.u.ip6,
693725
0, NULL,
694726
rpc->hsk->homa->priority_map[priority] << 5, 0);
@@ -703,6 +735,7 @@ void __homa_xmit_data(struct sk_buff *skb, struct homa_rpc *rpc)
703735
homa_get_skb_info(skb)->offset);
704736

705737
#ifndef __STRIP__ /* See strip.py */
738+
homa_set_hijack(skb, rpc->peer, false);
706739
rpc->hsk->inet.tos =
707740
rpc->hsk->homa->priority_map[priority] << 5;
708741
err = ip_queue_xmit(&rpc->hsk->inet.sk, skb, &rpc->peer->flow);

homa_wire.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -547,20 +547,6 @@ static inline u64 homa_local_id(__be64 sender_id)
547547
return be64_to_cpu(sender_id) ^ 1;
548548
}
549549

550-
#ifndef __STRIP__ /* See strip.py */
551-
/**
552-
* homa_set_hijack() - Set fields in a Homa header that are needed for
553-
* TCP hijacking to work properly.
554-
* @common: Header in which to set fields.
555-
*/
556-
static inline void homa_set_hijack(struct homa_common_hdr *common)
557-
{
558-
common->flags = HOMA_TCP_FLAGS;
559-
common->urgent = htons(HOMA_TCP_URGENT);
560-
common->doff = 0x50;
561-
}
562-
#endif /* See strip.py */
563-
564550
/**
565551
* homa_get_offset() - Returns the offset within message of the first byte
566552
* of data in a Homa DATA packet (the offset is stored in different places

test/mock.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,16 @@ int mock_signal_pending;
7070
/* Used as current task during tests. Also returned by kthread_run. */
7171
struct task_struct mock_task;
7272

73-
/* If a test sets this variable to nonzero, ip_queue_xmit will log
73+
/* If a test sets this variable to nonzero, ip*xmit will log
7474
* outgoing packets using the long format rather than short.
7575
*/
7676
int mock_xmit_log_verbose;
7777

78+
/* If a test sets this variable to nonzero, ip*xmit will log hijacking
79+
* information from outgoing packets.
80+
*/
81+
int mock_xmit_log_hijack;
82+
7883
/* If a test sets this variable to nonzero, calls to wake_up and
7984
* wake_up_all will be logged.
8085
*/
@@ -460,6 +465,13 @@ void __copy_overflow(int size, unsigned long count)
460465
abort();
461466
}
462467

468+
__sum16 csum_ipv6_magic(const struct in6_addr *saddr,
469+
const struct in6_addr *daddr,
470+
__u32 len, __u8 proto, __wsum csum)
471+
{
472+
return 0;
473+
}
474+
463475
#ifdef CONFIG_DEBUG_LOCK_ALLOC
464476
int debug_lockdep_rcu_enabled(void)
465477
{
@@ -775,6 +787,13 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
775787
else
776788
homa_print_packet_short(skb, buffer, sizeof(buffer));
777789
unit_log_printf("; ", "xmit %s", buffer);
790+
if (mock_xmit_log_hijack) {
791+
struct homa_common_hdr *h;
792+
793+
h = (struct homa_common_hdr *)skb_transport_header(skb);
794+
unit_log_printf("; ", "hijack checksum %d, flags 0x%x",
795+
h->checksum, h->flags);
796+
}
778797
kfree_skb(skb);
779798
return 0;
780799
}
@@ -802,6 +821,13 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
802821
else
803822
homa_print_packet_short(skb, buffer, sizeof(buffer));
804823
unit_log_printf("; ", "xmit %s", buffer);
824+
if (mock_xmit_log_hijack) {
825+
struct homa_common_hdr *h;
826+
827+
h = (struct homa_common_hdr *)skb_transport_header(skb);
828+
unit_log_printf("; ", "hijack checksum %d, flags 0x%x",
829+
h->checksum, h->flags);
830+
}
805831
kfree_skb(skb);
806832
return 0;
807833
}
@@ -1428,6 +1454,10 @@ void sk_skb_reason_drop(struct sock *sk, struct sk_buff *skb,
14281454
__kfree_skb(skb);
14291455
#endif
14301456
}
1457+
__wsum skb_checksum(const struct sk_buff *skb, int offset, int len, __wsum csum)
1458+
{
1459+
return 0;
1460+
}
14311461

14321462
int skb_copy_datagram_iter(const struct sk_buff *from, int offset,
14331463
struct iov_iter *iter, int size)
@@ -2428,6 +2458,7 @@ void mock_teardown(void)
24282458
mock_prepare_to_wait_status = -ERESTARTSYS;
24292459
mock_signal_pending = 0;
24302460
mock_xmit_log_verbose = 0;
2461+
mock_xmit_log_hijack = 0;
24312462
mock_log_wakeups = 0;
24322463
mock_mtu = 0;
24332464
mock_max_skb_frags = MAX_SKB_FRAGS;

test/mock.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@
104104

105105
#define spin_unlock mock_spin_unlock
106106

107+
#undef tcp_v4_check
108+
#define tcp_v4_check(...) (~(__force __sum16)444U)
109+
110+
#undef tcp_v6_check
111+
#define tcp_v6_check(...) (~(__force __sum16)666U)
112+
107113
#undef this_cpu_ptr
108114
#define this_cpu_ptr(name) (&name[cpu_number])
109115

@@ -180,6 +186,7 @@ extern int mock_trylock_errors;
180186
extern u64 mock_tt_cycles;
181187
extern int mock_vmalloc_errors;
182188
extern int mock_xmit_log_verbose;
189+
extern int mock_xmit_log_hijack;
183190

184191
extern struct task_struct *current_task;
185192

0 commit comments

Comments
 (0)