summaryrefslogtreecommitdiffstats
path: root/src/vnet/interface.api
blob: 14ff6d5a52235f2c4d1d7cba3920be65c0e3ebf2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/** \brief Set flags on the interface
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - index of the interface to set flags on
    @param admin_up_down - set the admin state, 1 = up, 0 = down
    @param link_up_down - Oper state sent on change event, not used in config.
    @param deleted - interface was deleted
*/
autoreply define sw_interface_set_flags
{
  u32 client_index;
  u32 context;
  u32 sw_if_index;
  /* 1 = up, 0 = down */
  u8 admin_up_down;
  u8 link_up_down;
  u8 deleted;
};

/** \brief Set interface MTU
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - index of the interface to set MTU on
    @param mtu - MTU
*/
autoreply define sw_interface_set_mtu
{
  u32 client_index;
  u32 context;
  u32 sw_if_index;
  u16 mtu;
};

/** \brief Register for interface events
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param enable_disable - 1 => register for events, 0 => cancel registration
    @param pid - sender's pid
*/
autoreply define want_interface_events
{
  u32 client_index;
  u32 context;
  u32 enable_disable;
  u32 pid;
};

/** \brief Interface details structure (fix this) 
    @param sw_if_index - index of the interface
    @param sup_sw_if_index - index of parent interface if any, else same as sw_if_index  
    @param l2_address_length - length of the interface's l2 address
    @param pid - the interface's l2 address
    @param interface_name - name of the interface
    @param link_duplex - 1 if half duplex, 2 if full duplex
    @param link_speed - 1 = 10M, 2 = 100M, 4 = 1G, 8 = 10G, 16 = 40G, 32 = 100G
    @param link_MTU - max. transmittion unit
    @param sub_if_id - A number 0-N to uniquely identify this subif on super if
    @param sub_dot1ad - 0 = dot1q, 1 = dot1ad
    @param sub_dot1ah - 1 = dot1ah, 0 = otherwise
    @param sub_number_of_tags - Number of tags (0 - 2)
    @param sub_outer_vlan_id
    @param sub_inner_vlan_id
    @param sub_exact_match
    @param sub_default
    @param sub_outer_vlan_id_any
    @param sub_inner_vlan_id_any
    @param vtr_op - vlan tag rewrite operation
    @param vtr_push_dot1q
    @param vtr_tag1
    @param vtr_tag2
    @param pbb_outer_tag - translate pbb s-tag
    @param pbb_b_dmac[6] - B-tag remote mac address
    @param pbb_b_smac[6] - B-tag local mac address
    @param pbb_b_vlanid - B-tag vlanid
    @param pbb_i_sid - I-tag service id
*/
define sw_interface_details
{
  u32 context;
  u32 sw_if_index;

  /* index of sup interface (e.g. hw interface).
     equal to sw_if_index for super hw interface. */
  u32 sup_sw_if_index;

  /* Layer 2 address, if applicable */
  u32 l2_address_length;
  u8 l2_address[8];

  /* Interface name */
  u8 interface_name[64];

  /* 1 = up, 0 = down */
  u8 admin_up_down;
  u8 link_up_down;

  /* 1 = half duplex, 2 = full duplex */
  u8 link_duplex;

  /* 1 = 10M, 2 = 100M, 4 = 1G, 8 = 10G, 16 = 40G, 32 = 100G */
  u8 link_speed;

  /* MTU */
  u16 link_mtu;

  /* Subinterface ID. A number 0-N to uniquely identify this subinterface under the super interface */
  u32 sub_id;

  /* 0 = dot1q, 1=dot1ad */
  u8 sub_dot1ad;
  /* 1 = dot1h, 1=otherwise */
  u8 sub_dot1ah;

  /* Number of tags 0-2 */
  u8 sub_number_of_tags;
  u16 sub_outer_vlan_id;
  u16 sub_inner_vlan_id;
  u8 sub_exact_match;
  u8 sub_default;
  u8 sub_outer_vlan_id_any;
  u8 sub_inner_vlan_id_any;

  /* vlan tag rewrite state */
  u32 vtr_op;
  u32 vtr_push_dot1q;		// ethertype of first pushed tag is dot1q/dot1ad
  u32 vtr_tag1;			// first pushed tag
  u32 vtr_tag2;			// second pushed tag
  u8 tag[64];
  
  /* pbb tag rewrite info */
  u16 outer_tag;
  u8  b_dmac[6];
  u8  b_smac[6];
  u16 b_vlanid;
  u32 i_sid;
};

/* works */
define sw_interface_dump
{
  u32 client_index;
  u32 context;
  u8 name_filter_valid;
  u8 name_filter[49];
};

/** \brief Set or delete one or all ip addresses on a specified interface
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - index of the interface to add/del addresses 
    @param is_add - add address if non-zero, else delete
    @param is_ipv6 - if non-zero the address is ipv6, else ipv4
    @param del_all - if non-zero delete all addresses on the interface
    @param address_length - address length in bytes, 4 for ip4, 16 for ip6
    @param address - array of address bytes
*/
autoreply define sw_interface_add_del_address
{
  u32 client_index;
  u32 context;
  u32 sw_if_index;
  u8 is_add;
  u8 is_ipv6;
  u8 del_all;
  u8 address_length;
  u8 address[16];
};

/** \brief Associate the specified interface with a fib table
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - index of the interface
    @param is_ipv6 - if non-zero ipv6, else ipv4
    @param vrf_id - fib table/vrd id to associate the interface with
*/
autoreply define sw_interface_set_table
{
  u32 client_index;
  u32 context;
  u32 sw_if_index;
  u8 is_ipv6;
  u32 vrf_id;
};

/** \brief Get VRF id assigned to interface
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - index of the interface
*/
define sw_interface_get_table
{
  u32 client_index;
  u32 context;
  u32 sw_if_index;
  u8 is_ipv6;
};

/** \brief Reply to get_sw_interface_vrf
    @param context - sender context which was passed in the request
    @param vrf_id - VRF id assigned to the interface
*/
define sw_interface_get_table_reply
{
  u32 context;
  i32 retval;
  u32 vrf_id;
};

typeonly manual_print manual_endian define vlib_counter
{
  u64 packets;			/**< packet counter */
  u64 bytes;			/**< byte counter  */
};

/** \brief Simple stats counters structure
    @param vnet_counter_type- such as ip4, ip6, punts, etc
    @param first_sw_if_index - first sw index in block of index, counts
    @param count - number of counters, equal to the number of interfaces in
      this stats block
    @param data - contiguous block of u64 counters
*/
manual_print manual_endian define vnet_interface_simple_counters
{
  /* enums - plural - in vnet/interface.h */
  u8 vnet_counter_type;
  u32 first_sw_if_index;
  u32 count;
  u64 data[count];
};

/** \brief Combined stats counters structure
    @param vnet_counter_type- such as ip4, ip6, punts, etc
    @param first_sw_if_index - first sw index in block of index, counts
    @param count - number of counters, equal to the number of interfaces in
      this stats block
    @param data - contiguous block of vlib_counter_t structures
*/
manual_print manual_endian define vnet_interface_combined_counters
{
  /* enums - plural - in vnet/interface.h */
  u8 vnet_counter_type;
  u32 first_sw_if_index;
  u32 count;
  vl_api_vlib_counter_t data[count];
};

/** \brief Set unnumbered interface add / del request
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - interface with an IP address
    @param unnumbered_sw_if_index - interface which will use the address
    @param is_add - if non-zero set the association, else unset it
*/
autoreply define sw_interface_set_unnumbered
{
  u32 client_index;
  u32 context;
  u32 sw_if_index;		/* use this intfc address */
  u32 unnumbered_sw_if_index;	/* on this interface */
  u8 is_add;
};

/** \brief Clear interface statistics
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - index of the interface to clear statistics
*/
autoreply define sw_interface_clear_stats
{
  u32 client_index;
  u32 context;
  u32 sw_if_index;
};

/** \brief Set / clear software interface tag
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - the interface
    @param add_del - 1 = add, 0 = delete
    @param tag - an ascii tag
*/
autoreply define sw_interface_tag_add_del 
{
    u32 client_index;
    u32 context;
    u8 is_add;
    u32 sw_if_index;
    u8 tag[64];
};

/** \brief Set an interface's MAC address
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param sw_if_index - the interface whose MAC will be set
    @param mac_addr - the new MAC address
*/
autoreply define sw_interface_set_mac_address
{
    u32 client_index;
    u32 context;
    u32 sw_if_index;
    u8 mac_address[6];
};

/*
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * @brief IPv6 to IPv4 translation */ #ifndef __included_ip6_to_ip4_h__ #define __included_ip6_to_ip4_h__ #include <vnet/ip/ip.h> /** * IPv6 to IPv4 set call back function type */ typedef int (*ip6_to_ip4_set_fn_t) (ip6_header_t * ip6, ip4_header_t * ip4, void *ctx); /* *INDENT-OFF* */ static u8 icmp6_to_icmp_updater_pointer_table[] = { 0, 1, ~0, ~0, 2, 2, 9, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 }; /* *INDENT-ON* */ #define frag_id_6to4(id) ((id) ^ ((id) >> 16)) /** * @brief Parse some useful information from IPv6 header. * * @param ip6 IPv6 header. * @param buff_len Buffer length. * @param l4_protocol L4 protocol number. * @param l4_offset L4 header offset. * @param frag_hdr_offset Fragment header offset if present, 0 otherwise. * * @returns 0 on success, non-zero value otherwise. */ static_always_inline int ip6_parse (const ip6_header_t * ip6, u32 buff_len, u8 * l4_protocol, u16 * l4_offset, u16 * frag_hdr_offset) { if (ip6->protocol == IP_PROTOCOL_IPV6_FRAGMENTATION) { *l4_protocol = ((ip6_frag_hdr_t *) (ip6 + 1))->next_hdr; *frag_hdr_offset = sizeof (*ip6); *l4_offset = sizeof (*ip6) + sizeof (ip6_frag_hdr_t); } else { *l4_protocol = ip6->protocol; *frag_hdr_offset = 0; *l4_offset = sizeof (*ip6); } return (buff_len < (*l4_offset + 4)) || (clib_net_to_host_u16 (ip6->payload_length) < (*l4_offset + 4 - sizeof (*ip6))); } /** * @brief Get TCP/UDP port number or ICMP id from IPv6 packet. * * @param ip6 IPv6 header. * @param sender 1 get sender port, 0 get receiver port. * @param buffer_len Buffer length. * * @returns Port number on success, 0 otherwise. */ always_inline u16 ip6_get_port (ip6_header_t * ip6, u8 sender, u16 buffer_len) { u8 l4_protocol; u16 l4_offset; u16 frag_offset; u8 *l4; if (ip6_parse (ip6, buffer_len, &l4_protocol, &l4_offset, &frag_offset)) return 0; if (frag_offset && ip6_frag_hdr_offset (((ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_offset)))) return 0; //Can't deal with non-first fragment for now l4 = u8_ptr_add (ip6, l4_offset); if (l4_protocol == IP_PROTOCOL_TCP || l4_protocol == IP_PROTOCOL_UDP) { return (sender) ? ((udp_header_t *) (l4))->src_port : ((udp_header_t *) (l4))->dst_port; } else if (l4_protocol == IP_PROTOCOL_ICMP6) { icmp46_header_t *icmp = (icmp46_header_t *) (l4); if (icmp->type == ICMP6_echo_request) { return (sender) ? ((u16 *) (icmp))[2] : -1; } else if (icmp->type == ICMP6_echo_reply) { return (sender) ? -1 : ((u16 *) (icmp))[2]; } } return 0; } /** * @brief Convert type and code value from ICMP6 to ICMP4. * * @param icmp ICMP header. * @param inner_ip6 Inner IPv6 header if present, 0 otherwise. * * @returns 0 on success, non-zero value otherwise. */ static_always_inline int icmp6_to_icmp_header (icmp46_header_t * icmp, ip6_header_t ** inner_ip6) { *inner_ip6 = NULL; switch (icmp->type) { case ICMP6_echo_request: icmp->type = ICMP4_echo_request; break; case ICMP6_echo_reply: icmp->type = ICMP4_echo_reply; break; case ICMP6_destination_unreachable: *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); switch (icmp->code) { case ICMP6_destination_unreachable_no_route_to_destination: //0 case ICMP6_destination_unreachable_beyond_scope_of_source_address: //2 case ICMP6_destination_unreachable_address_unreachable: //3 icmp->type = ICMP4_destination_unreachable; icmp->code = ICMP4_destination_unreachable_destination_unreachable_host; break; case ICMP6_destination_unreachable_destination_administratively_prohibited: //1 icmp->type = ICMP4_destination_unreachable; icmp->code = ICMP4_destination_unreachable_communication_administratively_prohibited; break; case ICMP6_destination_unreachable_port_unreachable: icmp->type = ICMP4_destination_unreachable; icmp->code = ICMP4_destination_unreachable_port_unreachable; break; default: return -1; } break; case ICMP6_packet_too_big: *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); icmp->type = ICMP4_destination_unreachable; icmp->code = 4; { u32 advertised_mtu = clib_net_to_host_u32 (*((u32 *) (icmp + 1))); advertised_mtu -= 20; //FIXME: = minimum(advertised MTU-20, MTU_of_IPv4_nexthop, (MTU_of_IPv6_nexthop)-20) ((u16 *) (icmp))[3] = clib_host_to_net_u16 (advertised_mtu); } break; case ICMP6_time_exceeded: *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); icmp->type = ICMP4_time_exceeded; break; case ICMP6_parameter_problem: *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); switch (icmp->code) { case ICMP6_parameter_problem_erroneous_header_field: icmp->type = ICMP4_parameter_problem; icmp->code = ICMP4_parameter_problem_pointer_indicates_error; u32 pointer = clib_net_to_host_u32 (*((u32 *) (icmp + 1))); if (pointer >= 40) return -1; ((u8 *) (icmp + 1))[0] = icmp6_to_icmp_updater_pointer_table[pointer]; break; case ICMP6_parameter_problem_unrecognized_next_header: icmp->type = ICMP4_destination_unreachable; icmp->code = ICMP4_destination_unreachable_port_unreachable; break; case ICMP6_parameter_problem_unrecognized_option: default: return -1; } break; default: return -1; break; } return 0; } /** * @brief Translate TOS value from IPv6 to IPv4. * * @param ip6 IPv6 header. * * @returns IPv4 TOS value. */ static_always_inline u8 ip6_translate_tos (const ip6_header_t * ip6) { return (clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label) & 0x0ff00000) >> 20; } /** * @brief Translate ICMP6 packet to ICMP4. * * @param p Buffer to translate. * @param fn The function to translate outer header. * @param ctx A context passed in the outer header translate function. * @param inner_fn The function to translate inner header. * @param inner_ctx A context passed in the inner header translate function. * * @returns 0 on success, non-zero value otherwise. */ always_inline int icmp6_to_icmp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx, ip6_to_ip4_set_fn_t inner_fn, void *inner_ctx) { ip6_header_t *ip6, *inner_ip6; ip4_header_t *ip4, *inner_ip4; u32 ip6_pay_len; icmp46_header_t *icmp; ip_csum_t csum; int rv; ip6 = vlib_buffer_get_current (p); ip6_pay_len = clib_net_to_host_u16 (ip6->payload_length); icmp = (icmp46_header_t *) (ip6 + 1); ASSERT (ip6_pay_len + sizeof (*ip6) <= p->current_length); //No extensions headers allowed here if (ip6->protocol != IP_PROTOCOL_ICMP6) return -1; //There are no fragmented ICMP messages, so no extension header for now if (icmp6_to_icmp_header (icmp, &inner_ip6)) return -1; if (inner_ip6) { u16 *inner_L4_checksum, inner_l4_offset, inner_frag_offset, inner_frag_id; u8 *inner_l4, inner_protocol; //We have two headers to translate // FROM // [ IPv6 ]<- ext ->[IC][ IPv6 ]<- ext ->[L4 header ... // Handled cases: // [ IPv6 ][IC][ IPv6 ][L4 header ... // [ IPv6 ][IC][ IPv6 ][Fr][L4 header ... // TO // [ IPv4][IC][ IPv4][L4 header ... if (ip6_parse (inner_ip6, ip6_pay_len - 8, &inner_protocol, &inner_l4_offset, &inner_frag_offset)) return -1; inner_l4 = u8_ptr_add (inner_ip6, inner_l4_offset); inner_ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -sizeof (*inner_ip4)); if (inner_frag_offset) { ip6_frag_hdr_t *inner_frag = (ip6_frag_hdr_t *) u8_ptr_add (inner_ip6, inner_frag_offset); inner_frag_id = frag_id_6to4 (inner_frag->identification); } else { inner_frag_id = 0; } //Do the translation of the inner packet if (inner_protocol == IP_PROTOCOL_TCP) { inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 16); } else if (inner_protocol == IP_PROTOCOL_UDP) { inner_L4_checksum = (u16 *) u8_ptr_add (inner_l4, 6); } else if (inner_protocol == IP_PROTOCOL_ICMP6) { icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4; //It cannot be of a different type as ip6_icmp_to_icmp6_in_place succeeded inner_icmp->type = (inner_icmp->type == ICMP6_echo_request) ? ICMP4_echo_request : ICMP4_echo_reply; inner_protocol = IP_PROTOCOL_ICMP; //Will be copied to ip6 later inner_L4_checksum = &inner_icmp->checksum; } else { return -1; } csum = *inner_L4_checksum; csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[0]); csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]); csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]); csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]); *inner_L4_checksum = ip_csum_fold (csum); if ((rv = inner_fn (inner_ip6, inner_ip4, inner_ctx)) != 0) return rv; inner_ip4->ip_version_and_header_length = IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS; inner_ip4->tos = ip6_translate_tos (inner_ip6); inner_ip4->length = u16_net_add (inner_ip6->payload_length, sizeof (*ip4) + sizeof (*ip6) - inner_l4_offset); inner_ip4->fragment_id = inner_frag_id; inner_ip4->flags_and_fragment_offset = clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS); inner_ip4->ttl = inner_ip6->hop_limit; inner_ip4->protocol = inner_protocol; inner_ip4->checksum = ip4_header_checksum (inner_ip4); if (inner_ip4->protocol == IP_PROTOCOL_ICMP) { //Recompute ICMP checksum icmp46_header_t *inner_icmp = (icmp46_header_t *) inner_l4; inner_icmp->checksum = 0; csum = ip_incremental_checksum (0, inner_icmp, clib_net_to_host_u16 (inner_ip4->length) - sizeof (*inner_ip4)); inner_icmp->checksum = ~ip_csum_fold (csum); } else { //Update to new pseudo-header csum = *inner_L4_checksum; csum = ip_csum_add_even (csum, inner_ip4->src_address.as_u32); csum = ip_csum_add_even (csum, inner_ip4->dst_address.as_u32); *inner_L4_checksum = ip_csum_fold (csum); } //Move up icmp header ip4 = (ip4_header_t *) u8_ptr_add (inner_l4, -2 * sizeof (*ip4) - 8); clib_memcpy (u8_ptr_add (inner_l4, -sizeof (*ip4) - 8), icmp, 8); icmp = (icmp46_header_t *) u8_ptr_add (inner_l4, -sizeof (*ip4) - 8); } else { //Only one header to translate ip4 = (ip4_header_t *) u8_ptr_add (ip6, sizeof (*ip6) - sizeof (*ip4)); } vlib_buffer_advance (p, (u32) (((u8 *) ip4) - ((u8 *) ip6))); if ((rv = fn (ip6, ip4, ctx)) != 0) return rv; ip4->ip_version_and_header_length = IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS; ip4->tos = ip6_translate_tos (ip6); ip4->fragment_id = 0; ip4->flags_and_fragment_offset = 0; ip4->ttl = ip6->hop_limit; ip4->protocol = IP_PROTOCOL_ICMP; //TODO fix the length depending on offset length ip4->length = u16_net_add (ip6->payload_length, (inner_ip6 == NULL) ? sizeof (*ip4) : (2 * sizeof (*ip4) - sizeof (*ip6))); ip4->checksum = ip4_header_checksum (ip4); //Recompute ICMP checksum icmp->checksum = 0; csum = ip_incremental_checksum (0, icmp, clib_net_to_host_u16 (ip4->length) - sizeof (*ip4)); icmp->checksum = ~ip_csum_fold (csum); return 0; } /** * @brief Translate IPv6 fragmented packet to IPv4. * * @param p Buffer to translate. * @param fn The function to translate header. * @param ctx A context passed in the header translate function. * * @returns 0 on success, non-zero value otherwise. */ always_inline int ip6_to_ip4_fragmented (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx) { ip6_header_t *ip6; ip6_frag_hdr_t *frag; ip4_header_t *ip4; u16 frag_id; u8 frag_more; u16 frag_offset; u8 l4_protocol; u16 l4_offset; int rv; ip6 = vlib_buffer_get_current (p); if (ip6_parse (ip6, p->current_length, &l4_protocol, &l4_offset, &frag_offset)) return -1; frag = (ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_offset); ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4)); vlib_buffer_advance (p, l4_offset - sizeof (*ip4)); frag_id = frag_id_6to4 (frag->identification); frag_more = ip6_frag_hdr_more (frag); frag_offset = ip6_frag_hdr_offset (frag); if ((rv = fn (ip6, ip4, ctx)) != 0) return rv; ip4->ip_version_and_header_length = IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS; ip4->tos = ip6_translate_tos (ip6); ip4->length = u16_net_add (ip6->payload_length, sizeof (*ip4) - l4_offset + sizeof (*ip6)); ip4->fragment_id = frag_id; ip4->flags_and_fragment_offset = clib_host_to_net_u16 (frag_offset | (frag_more ? IP4_HEADER_FLAG_MORE_FRAGMENTS : 0)); ip4->ttl = ip6->hop_limit; ip4->protocol = (l4_protocol == IP_PROTOCOL_ICMP6) ? IP_PROTOCOL_ICMP : l4_protocol; ip4->checksum = ip4_header_checksum (ip4); return 0; } /** * @brief Translate IPv6 UDP/TCP packet to IPv4. * * @param p Buffer to translate. * @param fn The function to translate header. * @param ctx A context passed in the header translate function. * * @returns 0 on success, non-zero value otherwise. */ always_inline int ip6_to_ip4_tcp_udp (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx, u8 udp_checksum) { ip6_header_t *ip6; u16 *checksum; ip_csum_t csum = 0; ip4_header_t *ip4; u16 fragment_id; u16 flags; u16 frag_offset; u8 l4_protocol; u16 l4_offset; int rv; ip6 = vlib_buffer_get_current (p); if (ip6_parse (ip6, p->current_length, &l4_protocol, &l4_offset, &frag_offset)) return -1; if (l4_protocol == IP_PROTOCOL_TCP) { tcp_header_t *tcp = ip6_next_header (ip6); checksum = &tcp->checksum; } else { udp_header_t *udp = ip6_next_header (ip6); checksum = &udp->checksum; //UDP checksum is optional over IPv4 if (!udp_checksum) goto no_csum; } csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]); csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]); csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]); csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]); *checksum = ip_csum_fold (csum); no_csum: ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4)); vlib_buffer_advance (p, l4_offset - sizeof (*ip4)); if (PREDICT_FALSE (frag_offset)) { //Only the first fragment ip6_frag_hdr_t *hdr = (ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_offset); fragment_id = frag_id_6to4 (hdr->identification); flags = clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS); } else { fragment_id = 0; flags = 0; } if ((rv = fn (ip6, ip4, ctx)) != 0) return rv; ip4->ip_version_and_header_length = IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS; ip4->tos = ip6_translate_tos (ip6); ip4->length = u16_net_add (ip6->payload_length, sizeof (*ip4) + sizeof (*ip6) - l4_offset); ip4->fragment_id = fragment_id; ip4->flags_and_fragment_offset = flags; ip4->ttl = ip6->hop_limit; ip4->protocol = l4_protocol; ip4->checksum = ip4_header_checksum (ip4); //UDP checksum is optional over IPv4 if (!udp_checksum && l4_protocol == IP_PROTOCOL_UDP) { *checksum = 0; } else { csum = ip_csum_add_even (*checksum, ip4->dst_address.as_u32); csum = ip_csum_add_even (csum, ip4->src_address.as_u32); *checksum = ip_csum_fold (csum); } return 0; } #endif /* __included_ip6_to_ip4_h__ */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */