annotate src/event/quic/bpf/ngx_quic_reuseport_helper.c @ 9196:6c8595b77e66

QUIC: path aware in-flight bytes accounting. On-packet acknowledgement is made path aware, as per RFC 9000, Section 9.4: Packets sent on the old path MUST NOT contribute to congestion control or RTT estimation for the new path. To make this possible in a single congestion control context, the first packet to be sent after the new path has been validated, which includes resetting the congestion controller and RTT estimator, is now remembered in the connection. Packets sent previously, such as on the old path, are not taken into account. Note that although the packet number is saved per-connection, the added checks affect application level packets only. For non-application level packets, which are only processed prior to the handshake is complete, the remembered packet number remains set to zero.
author Sergey Kandaurov <pluknet@nginx.com>
date Tue, 12 Dec 2023 20:21:12 +0400
parents a2fbae359828
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
8676
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
1 #include <errno.h>
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
2 #include <linux/string.h>
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
3 #include <linux/udp.h>
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
4 #include <linux/bpf.h>
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
5 /*
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
6 * the bpf_helpers.h is not included into linux-headers, only available
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
7 * with kernel sources in "tools/lib/bpf/bpf_helpers.h" or in libbpf.
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
8 */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
9 #include <bpf/bpf_helpers.h>
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
10
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
11
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
12 #if !defined(SEC)
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
13 #define SEC(NAME) __attribute__((section(NAME), used))
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
14 #endif
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
15
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
16
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
17 #if defined(LICENSE_GPL)
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
18
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
19 /*
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
20 * To see debug:
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
21 *
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
22 * echo 1 > /sys/kernel/debug/tracing/events/bpf_trace/enable
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
23 * cat /sys/kernel/debug/tracing/trace_pipe
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
24 * echo 0 > /sys/kernel/debug/tracing/events/bpf_trace/enable
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
25 */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
26
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
27 #define debugmsg(fmt, ...) \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
28 do { \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
29 char __buf[] = fmt; \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
30 bpf_trace_printk(__buf, sizeof(__buf), ##__VA_ARGS__); \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
31 } while (0)
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
32
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
33 #else
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
34
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
35 #define debugmsg(fmt, ...)
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
36
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
37 #endif
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
38
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
39 char _license[] SEC("license") = LICENSE;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
40
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
41 /*****************************************************************************/
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
42
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
43 #define NGX_QUIC_PKT_LONG 0x80 /* header form */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
44 #define NGX_QUIC_SERVER_CID_LEN 20
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
45
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
46
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
47 #define advance_data(nbytes) \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
48 offset += nbytes; \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
49 if (start + offset > end) { \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
50 debugmsg("cannot read %ld bytes at offset %ld", nbytes, offset); \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
51 goto failed; \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
52 } \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
53 data = start + offset - 1;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
54
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
55
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
56 #define ngx_quic_parse_uint64(p) \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
57 (((__u64)(p)[0] << 56) | \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
58 ((__u64)(p)[1] << 48) | \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
59 ((__u64)(p)[2] << 40) | \
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
60 ((__u64)(p)[3] << 32) | \
8720
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
61 ((__u64)(p)[4] << 24) | \
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
62 ((__u64)(p)[5] << 16) | \
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
63 ((__u64)(p)[6] << 8) | \
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
64 ((__u64)(p)[7]))
8676
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
65
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
66 /*
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
67 * actual map object is created by the "bpf" system call,
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
68 * all pointers to this variable are replaced by the bpf loader
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
69 */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
70 struct bpf_map_def SEC("maps") ngx_quic_sockmap;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
71
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
72
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
73 SEC(PROGNAME)
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
74 int ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx)
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
75 {
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
76 int rc;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
77 __u64 key;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
78 size_t len, offset;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
79 unsigned char *start, *end, *data, *dcid;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
80
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
81 start = ctx->data;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
82 end = (unsigned char *) ctx->data_end;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
83 offset = 0;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
84
8720
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
85 advance_data(sizeof(struct udphdr)); /* data at UDP header */
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
86 advance_data(1); /* data at QUIC flags */
8676
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
87
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
88 if (data[0] & NGX_QUIC_PKT_LONG) {
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
89
8720
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
90 advance_data(4); /* data at QUIC version */
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
91 advance_data(1); /* data at DCID len */
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
92
8676
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
93 len = data[0]; /* read DCID length */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
94
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
95 if (len < 8) {
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
96 /* it's useless to search for key in such short DCID */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
97 return SK_PASS;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
98 }
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
99
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
100 } else {
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
101 len = NGX_QUIC_SERVER_CID_LEN;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
102 }
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
103
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
104 dcid = &data[1];
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
105 advance_data(len); /* we expect the packet to have full DCID */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
106
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
107 /* make verifier happy */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
108 if (dcid + sizeof(__u64) > end) {
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
109 goto failed;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
110 }
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
111
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
112 key = ngx_quic_parse_uint64(dcid);
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
113
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
114 rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0);
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
115
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
116 switch (rc) {
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
117 case 0:
8720
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
118 debugmsg("nginx quic socket selected by key 0x%llx", key);
8676
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
119 return SK_PASS;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
120
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
121 /* kernel returns positive error numbers, errno.h defines positive */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
122 case -ENOENT:
8720
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
123 debugmsg("nginx quic default route for key 0x%llx", key);
8676
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
124 /* let the default reuseport logic decide which socket to choose */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
125 return SK_PASS;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
126
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
127 default:
8720
1a489587e1c8 QUIC: fixed key extraction in bpf.
Vladimir Homutov <vl@nginx.com>
parents: 8676
diff changeset
128 debugmsg("nginx quic bpf_sk_select_reuseport err: %d key 0x%llx",
9015
a2fbae359828 QUIC: fixed indentation.
Sergey Kandaurov <pluknet@nginx.com>
parents: 8720
diff changeset
129 rc, key);
8676
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
130 goto failed;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
131 }
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
132
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
133 failed:
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
134 /*
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
135 * SK_DROP will generate ICMP, but we may want to process "invalid" packet
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
136 * in userspace quic to investigate further and finally react properly
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
137 * (maybe ignore, maybe send something in response or close connection)
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
138 */
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
139 return SK_PASS;
7df607cb2d11 QUIC: ngx_quic_bpf module.
Vladimir Homutov <vl@nginx.com>
parents:
diff changeset
140 }