src/switch/datapath/action_executor.c
/*
* Copyright (C) 2012-2013 NEC Corporation
*
* 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.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include "action_executor.h"
#include "async_event_notifier.h"
#include "flow_entry.h"
#include "group_table.h"
#include "packet_buffer.h"
#include "port_manager.h"
#include "table_manager.h"
#include "pipeline.h"
#define REMAINED_BUFFER_LENGTH( buf, ptr ) \
( buf->length - ( size_t ) ( ( char * ) ptr - ( char * ) buf->data ) )
OFDPE
init_action_executor() {
return OFDPE_SUCCESS;
}
OFDPE
finalize_action_executor() {
return OFDPE_SUCCESS;
}
static void
set_ipv4_checksum( ipv4_header_t *header ) {
assert( header != NULL );
header->csum = 0;
header->csum = get_checksum( ( uint16_t * ) header, ( uint32_t ) sizeof( ipv4_header_t ) );
}
static uint32_t
get_sum( uint16_t *pos, size_t size ) {
assert( pos != NULL );
uint32_t sum = 0;
for (; 2 <= size; pos++, size -= 2 ) {
sum += *pos;
}
if ( size == 1 ) {
union {
uint8_t buf[ 2 ];
uint16_t num;
} tail = { .buf = { *( uint8_t * ) pos, 0 } };
sum += tail.num;
}
return sum;
}
static uint16_t
get_checksum_from_sum( uint32_t sum ) {
// ones' complement: sum up carry
while ( sum & 0xffff0000 ) {
sum = ( sum & 0x0000ffff ) + ( sum >> 16 );
}
return ( uint16_t ) ~sum;
}
static uint32_t
get_ipv4_pseudo_header_sum( ipv4_header_t *header, uint8_t protocol, size_t payload_size ) {
assert( header != NULL );
uint32_t sum = 0;
sum += get_sum( ( uint16_t * ) &header->saddr, sizeof( header->saddr ) );
sum += get_sum( ( uint16_t * ) &header->daddr, sizeof( header->saddr ) );
union {
uint8_t buf[ 2 ];
uint16_t num;
} protocol_field = { .buf = { 0, protocol } };
sum += protocol_field.num;
sum += htons( ( uint16_t ) payload_size );
return sum;
}
static uint32_t
get_ipv6_pseudo_header_sum( ipv6_header_t *header, uint8_t protocol, size_t payload_size ) {
assert( header != NULL );
uint32_t sum = 0;
sum += get_sum( ( uint16_t * ) &header->saddr[ 0 ], sizeof( header->saddr ) );
sum += get_sum( ( uint16_t * ) &header->daddr[ 0 ], sizeof( header->saddr ) );
union {
uint8_t buf[ 2 ];
uint16_t num;
} protocol_field = { .buf = { 0, protocol } };
sum += protocol_field.num;
sum += htons( ( uint16_t ) payload_size );
return sum;
}
static uint32_t
get_icmpv6_pseudo_header_sum( ipv6_header_t *header, size_t payload_size ) {
assert( header != NULL );
uint32_t sum = 0;
sum += get_sum( ( uint16_t * ) &header->saddr[ 0 ], sizeof( header->saddr ) );
sum += get_sum( ( uint16_t * ) &header->daddr[ 0 ], sizeof( header->saddr ) );
union {
uint8_t buf[ 2 ];
uint16_t num;
} protocol_field = { .buf = { 0, IPPROTO_ICMPV6 } };
sum += protocol_field.num;
sum += htons( ( uint16_t ) payload_size );
return sum;
}
static void
set_ipv4_udp_checksum( ipv4_header_t *ipv4_header, udp_header_t *udp_header, void *payload ) {
assert( ipv4_header != NULL );
assert( udp_header != NULL );
uint32_t sum = 0;
sum += get_ipv4_pseudo_header_sum( ipv4_header, IPPROTO_UDP, ntohs( udp_header->len ) );
udp_header->csum = 0;
sum += get_sum( ( uint16_t * ) udp_header, sizeof( udp_header_t ) );
if ( payload != NULL ) {
sum += get_sum( payload, ntohs( udp_header->len ) - sizeof( udp_header_t ) );
}
udp_header->csum = get_checksum_from_sum( sum );
}
static void
set_ipv6_udp_checksum( ipv6_header_t *ipv6_header, udp_header_t *udp_header, void *payload ) {
assert( ipv6_header != NULL );
assert( udp_header != NULL );
uint32_t sum = 0;
sum += get_ipv6_pseudo_header_sum( ipv6_header, IPPROTO_UDP, ntohs( udp_header->len ) );
udp_header->csum = 0;
sum += get_sum( ( uint16_t * ) udp_header, sizeof( udp_header_t ) );
if ( payload != NULL ) {
sum += get_sum( payload, ntohs( udp_header->len ) - sizeof( udp_header_t ) );
}
udp_header->csum = get_checksum_from_sum( sum );
}
static void
set_ipv4_tcp_checksum( ipv4_header_t *ipv4_header, tcp_header_t *tcp_header, void *tcp_payload, size_t tcp_payload_length ) {
assert( ipv4_header != NULL );
assert( tcp_header != NULL );
uint32_t sum = 0;
sum += get_ipv4_pseudo_header_sum( ipv4_header, IPPROTO_TCP, ( size_t ) tcp_header->offset * 4 + tcp_payload_length );
tcp_header->csum = 0;
sum += get_sum( ( uint16_t * ) tcp_header, sizeof( tcp_header_t ) );
if ( tcp_payload != NULL && tcp_payload_length > 0 ) {
sum += get_sum( tcp_payload, tcp_payload_length );
}
tcp_header->csum = get_checksum_from_sum( sum );
}
static void
set_ipv6_tcp_checksum( ipv6_header_t *ipv6_header, tcp_header_t *tcp_header, void *tcp_payload, size_t tcp_payload_length ) {
assert( ipv6_header != NULL );
assert( tcp_header != NULL );
uint32_t sum = 0;
sum += get_ipv6_pseudo_header_sum( ipv6_header, IPPROTO_TCP, ( size_t ) tcp_header->offset * 4 + tcp_payload_length );
tcp_header->csum = 0;
sum += get_sum( ( uint16_t * ) tcp_header, sizeof( tcp_header_t ) );
if ( tcp_payload != NULL && tcp_payload_length > 0 ) {
sum += get_sum( tcp_payload, tcp_payload_length );
}
tcp_header->csum = get_checksum_from_sum( sum );
}
static void
set_icmpv4_checksum( icmp_header_t *icmp_header, size_t icmp_length ) {
assert( icmp_header != NULL );
icmp_header->csum = 0;
icmp_header->csum = get_checksum( ( uint16_t * ) icmp_header, ( uint32_t ) icmp_length );
}
static void
set_icmpv6_checksum( ipv6_header_t *ipv6_header, icmpv6_header_t *icmp_header, size_t length ) {
assert( ipv6_header != NULL );
assert( icmp_header != NULL );
uint32_t sum = 0;
sum += get_icmpv6_pseudo_header_sum( ipv6_header, sizeof( icmpv6_header_t ) + length );
icmp_header->csum = 0;
sum += get_sum( ( uint16_t * ) icmp_header, sizeof( icmpv6_header_t ) + length );
icmp_header->csum = get_checksum_from_sum( sum );
}
static void
set_sctp_checksum( sctp_header_t *sctp_header, size_t sctp_length ) {
assert( sctp_header != NULL );
sctp_header->checksum = 0;
// RFC 3309
uint32_t crc_c[256] = {
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
};
uint8_t *ptr = ( uint8_t * ) sctp_header;
uint32_t crc32 = 0xffffffff;
for ( size_t i = 0; i < sctp_length; i++ ) {
crc32 = ( crc32 >> 8 ) ^ crc_c[ ( crc32 ^ ( ptr[ i ] ) ) & 0xFF ];
}
crc32 = ( ~crc32 ) & 0xffffffff;
uint8_t *csum = ( uint8_t * ) &sctp_header->checksum;
csum[ 0 ] = ( uint8_t ) ( ( crc32 >> 0 ) & 0xFF );
csum[ 1 ] = ( uint8_t ) ( ( crc32 >> 8 ) & 0xFF );
csum[ 2 ] = ( uint8_t ) ( ( crc32 >> 16 ) & 0xFF );
csum[ 3 ] = ( uint8_t ) ( ( crc32 >> 24 ) & 0xFF );
}
static bool
parse_frame( buffer *frame ) {
assert( frame != NULL );
uint32_t eth_in_port = 0;
uint64_t metadata = 0;
uint64_t tunnel_id = 0;
if ( frame->user_data != NULL ) {
packet_info *info = ( packet_info * ) frame->user_data;
eth_in_port = info->eth_in_port;
metadata = info->metadata;
tunnel_id = info->tunnel_id;
free_packet_info( frame );
}
bool ret = parse_packet( frame );
if ( !ret ) {
error( "Failed to parse an Ethernet frame." );
return false;
}
assert( frame->user_data != NULL );
{
packet_info *info = ( packet_info * ) frame->user_data;
info->eth_in_port = eth_in_port;
info->metadata = metadata;
info->tunnel_id = tunnel_id;
}
return true;
}
static packet_info *
get_packet_info_data( const buffer *frame ) {
assert( frame != NULL );
return ( packet_info * ) frame->user_data;
}
static void
set_address( void *dst, match8 *value, size_t size ) {
assert( dst != NULL );
assert( value != NULL );
uint8_t *tmp = ( uint8_t * ) dst;
for ( size_t i = 0; i < size; i++ ) {
tmp[ i ] = value->value;
value++;
}
}
static void
set_dl_address( void *dst, match8 *value ) {
assert( dst != NULL );
assert( value != NULL );
set_address( dst, value, ETH_ADDRLEN );
}
static void
set_ipv6_address( void *dst, match8 *value ) {
assert( dst != NULL );
assert( value != NULL );
set_address( dst, value, IPV6_ADDRLEN );
}
static bool
set_dl_dst( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ether( frame ) ) {
warn( "A non-ethernet frame (%#x) found while setting the destination data link address.", info->format );
return true;
}
ether_header_t *header = info->l2_header;
set_dl_address( header->macda, value );
return parse_frame( frame );
}
static bool
set_dl_src( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ether( frame ) ) {
warn( "A non-ethernet frame (%#x) found while setting the source data link address.", info->format );
return true;
}
ether_header_t *header = info->l2_header;
set_dl_address( header->macsa, value );
return parse_frame( frame );
}
static bool
set_dl_type( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ether( frame ) ) {
warn( "A non-ethernet frame (%#x) found while setting the data link type.", info->format );
return true;
}
ether_header_t *header = info->l2_header;
header->type = htons( value );
return parse_frame( frame );
}
static bool
set_vlan_vid( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_vtag( frame ) ) {
warn( "A non-vlan frame (%#x) found while setting the vlan-id.", info->format );
return true;
}
vlantag_header_t *header = info->l2_vlan_header;
header->tci = ( uint16_t ) ( ( header->tci & htons( 0xf000 ) ) | htons( value & 0x0fff ) );
return parse_frame( frame );
}
static bool
set_vlan_pcp( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_vtag( frame ) ) {
warn( "A non-vlan frame (%#x) found while setting the priority code point.", info->format );
return true;
}
vlantag_header_t *header = info->l2_vlan_header;
uint16_t tci = ( uint16_t ) ( ( value & 0x07 ) << 13 );
header->tci = ( uint16_t ) ( ( header->tci & htons( 0x1fff ) ) | htons( tci ) );
return parse_frame( frame );
}
static bool
set_nw_dscp( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( packet_type_ipv4( frame ) ){
ipv4_header_t *header = info->l3_header;
header->tos = ( uint8_t ) ( ( header->tos & 0x03 ) | ( ( value << 2 ) & 0xFC ) );
// no tcp/udp/icmp checksum caculation here because tos field is not included in pseudo header
set_ipv4_checksum( header );
}
else if ( packet_type_ipv6( frame ) ) {
ipv6_header_t *header = info->l3_header;
uint32_t hdrctl = ntohl( header->hdrctl );
header->hdrctl = htonl( ( hdrctl & 0xF03FFFFF ) + ( ( 0x3FU & value ) << 22 ) );
}
else {
warn( "A non-ipv4,ipv6 packet (%#x) found while setting the dscp field.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_nw_ecn( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( packet_type_ipv4( frame ) ) {
ipv4_header_t *header = info->l3_header;
header->tos = ( uint8_t ) ( ( header->tos & 0xFC ) | ( value & 0x03 ) );
// no tcp/udp/icmp checksum caculation here because tos field is not included in pseudo header
set_ipv4_checksum( header );
}
else if ( packet_type_ipv6( frame ) ) {
ipv6_header_t *header = info->l3_header;
// set Traffic Class
uint32_t hdrctl = ntohl(header->hdrctl);
header->hdrctl = htonl( ( hdrctl & 0xFFcFFFFF ) + ( ( 0x03U & value ) << 20 ) );
}
else {
warn( "A non-ipv4,ipv6 packet (%#x) found while setting the ecn field.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_ip_proto( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( packet_type_ipv4( frame ) ) {
ipv4_header_t *header = info->l3_header;
header->protocol = value;
set_ipv4_checksum( header );
// It is hard to calculate tcp/udp/icmp checksum caculation here, as we might break the payload.
}
else if ( packet_type_ipv6( frame ) ) {
ipv6_header_t *header = info->l3_header;
header->nexthdr = value;
// It is hard to calculate tcp/udp/icmp checksum caculation here, as we might break the payload.
}
else {
warn( "A non-ipv4,ipv6 packet (%#x) found while setting the ip_proto field.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_ipv4_src( buffer *frame, uint32_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ipv4( frame ) ) {
warn( "A non-ipv4 packet (%#x) found while setting the source address.", info->format );
return true;
}
ipv4_header_t *header = info->l3_header;
header->saddr = htonl( value );
if ( packet_type_ipv4_tcp( frame ) ) {
set_ipv4_tcp_checksum( info->l3_header, ( tcp_header_t * ) info->l4_header, info->l4_payload, info->l4_payload_length );
}
else if ( packet_type_ipv4_udp( frame ) ) {
set_ipv4_udp_checksum( info->l3_header, ( udp_header_t * ) info->l4_header, info->l4_payload );
}
else if ( packet_type_icmpv4( frame ) ) {
set_icmpv4_checksum( ( icmp_header_t * ) info->l4_header, info->l3_payload_length );
}
set_ipv4_checksum( header );
return parse_frame( frame );
}
static bool
set_ipv4_dst( buffer *frame, uint32_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ipv4( frame ) ) {
warn( "A non-ipv4 packet (%#x) found when setting the destination address.", info->format );
return true;
}
ipv4_header_t *header = info->l3_header;
header->daddr = htonl( value );
if ( packet_type_ipv4_tcp( frame ) ) {
set_ipv4_tcp_checksum( info->l3_header, ( tcp_header_t * ) info->l4_header, info->l4_payload, info->l4_payload_length );
}
else if ( packet_type_ipv4_udp( frame ) ) {
set_ipv4_udp_checksum( info->l3_header, ( udp_header_t * ) info->l4_header, info->l4_payload );
}
else if ( packet_type_icmpv4( frame ) ) {
set_icmpv4_checksum( ( icmp_header_t * ) info->l4_header, info->l3_payload_length );
}
set_ipv4_checksum( header );
return parse_frame( frame );
}
static bool
set_tcp_src( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
tcp_header_t *tcp_header = info->l4_header;
if ( packet_type_ipv4_tcp( frame ) ) {
tcp_header->src_port = htons( value );
set_ipv4_tcp_checksum( info->l3_header, tcp_header, info->l4_payload, info->l4_payload_length );
}
else if ( packet_type_ipv6_tcp( frame ) ) {
tcp_header->src_port = htons( value );
set_ipv6_tcp_checksum( info->l3_header, tcp_header, info->l4_payload, info->l4_payload_length );
}
else {
warn( "A non-tcp packet (%#x) found while setting the tcp source port.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_tcp_dst( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
tcp_header_t *tcp_header = info->l4_header;
if ( packet_type_ipv4_tcp( frame ) ) {
tcp_header->dst_port = htons( value );
set_ipv4_tcp_checksum( info->l3_header, tcp_header, info->l4_payload, info->l4_payload_length );
}
else if ( packet_type_ipv6_tcp( frame ) ) {
tcp_header->dst_port = htons( value );
set_ipv6_tcp_checksum( info->l3_header, tcp_header, info->l4_payload, info->l4_payload_length );
}
else {
warn( "A non-tcp packet (%#x) found while setting the tcp destination port.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_udp_src( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
udp_header_t *udp_header = info->l4_header;
if ( packet_type_ipv4_udp( frame ) ) {
udp_header->src_port = htons( value );
set_ipv4_udp_checksum( info->l3_header, udp_header, info->l4_payload );
}
else if ( packet_type_ipv6_udp( frame ) ) {
udp_header->src_port = htons( value );
set_ipv6_udp_checksum( info->l3_header, udp_header, info->l4_payload );
}
else {
warn( "A non-udp packet (%#x) found while setting the udp source port.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_udp_dst( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
udp_header_t *udp_header = info->l4_header;
if ( packet_type_ipv4_udp( frame ) ) {
udp_header->dst_port = htons( value );
set_ipv4_udp_checksum( info->l3_header, udp_header, info->l4_payload );
}
else if ( packet_type_ipv6_udp( frame ) ) {
udp_header->dst_port = htons( value );
set_ipv6_udp_checksum( info->l3_header, udp_header, info->l4_payload );
}
else {
warn( "A non-udp packet (%#x) found while setting the udp destination port.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_sctp_src( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
sctp_header_t *sctp_header = info->l4_header;
if ( packet_type_ipv4_sctp( frame ) ) {
sctp_header->src_port = htons( value );
set_sctp_checksum( sctp_header, info->l3_payload_length );
set_ipv4_checksum( ( ipv4_header_t * ) info->l3_header );
}
else if ( packet_type_ipv6_sctp( frame ) ) {
sctp_header->src_port = htons( value );
set_sctp_checksum( sctp_header, info->l3_payload_length );
}
else {
warn( "A non-sctp packet (%#x) found while setting the sctp source port.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_sctp_dst( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
sctp_header_t *sctp_header = info->l4_header;
if ( packet_type_ipv4_sctp( frame ) ) {
sctp_header->dst_port = htons( value );
set_sctp_checksum( sctp_header, info->l3_payload_length );
set_ipv4_checksum( ( ipv4_header_t * ) info->l3_header );
}
else if ( packet_type_ipv6_sctp( frame ) ) {
sctp_header->dst_port = htons( value );
set_sctp_checksum( sctp_header, info->l3_payload_length );
}
else {
warn( "A non-sctp packet (%#x) found while setting the sctp destination port.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
set_icmpv4_type( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_icmpv4( frame ) ) {
warn( "A non-icmpv4 packet (%#x) found while setting the type field.", info->format );
return true;
}
icmp_header_t *icmp_header = info->l4_header;
icmp_header->type = value;
set_icmpv4_checksum( icmp_header, info->l3_payload_length );
return parse_frame( frame );
}
static bool
set_icmpv4_code( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_icmpv4( frame ) ) {
warn( "A non-icmpv4 packet (%#x) found while setting the code field.", info->format );
return true;
}
icmp_header_t *icmp_header = info->l4_header;
icmp_header->code = value;
set_icmpv4_checksum( icmp_header, info->l3_payload_length );
return parse_frame( frame );
}
static bool
set_arp_op( buffer *frame, uint16_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_arp( frame ) ) {
warn( "A non-arp packet (%#x) found while setting the opcode field.", info->format );
return true;
}
arp_header_t *header = info->l3_header;
header->ar_op = htons( value );
return parse_frame( frame );
}
static bool
set_arp_spa( buffer *frame, uint32_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_arp( frame ) ) {
warn( "A non-arp packet (%#x) found while setting the source ip address.", info->format );
return true;
}
arp_header_t *header = info->l3_header;
header->sip = htonl( value );
return parse_frame( frame );
}
static bool
set_arp_tpa( buffer *frame, uint32_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_arp( frame ) ) {
warn( "A non-arp packet (%#x) found while setting the destination (target) ip address.", info->format );
return true;
}
arp_header_t *header = info->l3_header;
header->tip = htonl( value );
return parse_frame( frame );
}
static bool
set_arp_sha( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_arp( frame ) ) {
warn( "A non-arp packet (%#x) found while setting the source mac address.", info->format );
return true;
}
arp_header_t *header = info->l3_header;
set_dl_address( header->sha, value );
return parse_frame( frame );
}
static bool
set_arp_tha( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_arp( frame ) ) {
warn( "A non-arp packet (%#x) found while setting the destination (target) mac address.", info->format );
return true;
}
arp_header_t *header = info->l3_header;
set_dl_address( header->tha, value );
return parse_frame( frame );
}
static bool
set_ipv6_src( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ipv6( frame ) ) {
warn( "A non-ipv6 packet (%#x) found while setting the source ip address.", info->format );
return true;
}
ipv6_header_t *header = info->l3_header;
set_ipv6_address( header->saddr, value );
if ( packet_type_ipv6_tcp( frame ) ) {
set_ipv6_tcp_checksum( info->l3_header, ( tcp_header_t * ) info->l4_header, info->l4_payload, info->l4_payload_length );
}
else if ( packet_type_ipv6_udp( frame ) ) {
set_ipv6_udp_checksum( info->l3_header, ( udp_header_t * ) info->l4_header, info->l4_payload );
}
else if ( packet_type_icmpv6( frame ) ) {
set_icmpv6_checksum( info->l3_header, ( icmpv6_header_t * ) info->l4_header, info->l4_payload_length );
}
return parse_frame( frame );
}
static bool
set_ipv6_dst( buffer *frame, match8 value[] ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ipv6( frame ) ) {
warn( "A non-ipv6 packet (%#x) found while setting the destination ip address.", info->format );
return true;
}
ipv6_header_t *header = info->l3_header;
set_ipv6_address( header->daddr, value );
if ( packet_type_ipv6_tcp( frame ) ) {
set_ipv6_tcp_checksum( info->l3_header, ( tcp_header_t * ) info->l4_header, info->l4_payload, info->l4_payload_length );
}
else if ( packet_type_ipv6_udp( frame ) ) {
set_ipv6_udp_checksum( info->l3_header, ( udp_header_t * ) info->l4_header, info->l4_payload );
}
else if ( packet_type_icmpv6( frame ) ) {
set_icmpv6_checksum( info->l3_header, ( icmpv6_header_t * ) info->l4_header, info->l4_payload_length );
}
return parse_frame( frame );
}
static bool
set_ipv6_flabel( buffer *frame, uint32_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_ipv6( frame ) ) {
warn( "A non-ipv6 packet (%#x) found while setting the flow label.", info->format );
return true;
}
ipv6_header_t *header = info->l3_header;
header->hdrctl = ( header->hdrctl & htonl( 0xfff00000 ) ) | ( htonl( value ) & htonl( 0x000fffff ) );
return parse_frame( frame );
}
static bool
set_icmpv6_type( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_icmpv6( frame ) ) {
warn( "A non-icmpv6 packet (%#x) found while setting the type field.", info->format );
return true;
}
icmpv6_header_t *icmp_header = info->l4_header;
icmp_header->type = value;
set_icmpv6_checksum( info->l3_header, icmp_header, info->l4_payload_length );
return parse_frame( frame );
}
static bool
set_icmpv6_code( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_icmpv6( frame ) ) {
warn( "A non-icmpv6 packet (%#x) found while setting the code field.", info->format );
return true;
}
icmpv6_header_t *icmp_header = info->l4_header;
icmp_header->code = value;
set_icmpv6_checksum( info->l3_header, icmp_header, info->l4_payload_length );
return parse_frame( frame );
}
static bool
set_ipv6_nd_target( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_icmpv6( frame ) ) {
warn( "A non-icmpv6 packet (%#x) found while setting the neighbor target address.", info->format );
return true;
}
if ( ( info->icmpv6_type != ICMPV6_TYPE_NEIGHBOR_SOL )
&& ( info->icmpv6_type != ICMPV6_TYPE_NEIGHBOR_ADV ) ) {
warn( "Unsupported icmpv6 type (%#x) found while setting the neighbor target address.", info->icmpv6_type );
return true;
}
icmpv6_header_t *header = info->l3_payload;
icmpv6data_ndp_t *icmpv6data_ndp = ( icmpv6data_ndp_t * ) header->data;
set_ipv6_address( icmpv6data_ndp->nd_target, value );
set_icmpv6_checksum( info->l3_header, header, info->l4_payload_length );
return parse_frame( frame );
}
static bool
set_ipv6_nd_sll( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_icmpv6( frame ) ) {
warn( "A non-icmpv6 packet (%#x) found while setting the source link-layer address.", info->format );
return true;
}
if ( info->icmpv6_type != ICMPV6_TYPE_NEIGHBOR_SOL ) {
warn( "Unsupported icmpv6 type (%#x) found while setting the source link-layer address.", info->icmpv6_type );
return true;
}
icmpv6_header_t *header = info->l3_payload;
icmpv6data_ndp_t *icmpv6data_ndp = ( icmpv6data_ndp_t * ) header->data;
if ( icmpv6data_ndp->ll_type != ICMPV6_ND_SOURCE_LINK_LAYER_ADDRESS ) {
warn( "Incorrect link-layer address type (%#x) found while setting the source link-layer address.",
icmpv6data_ndp->ll_type );
return true;
}
set_dl_address( icmpv6data_ndp->ll_addr, value );
set_icmpv6_checksum( info->l3_header, header, info->l4_payload_length );
return parse_frame( frame );
}
static bool
set_ipv6_nd_tll( buffer *frame, match8 *value ) {
assert( frame != NULL );
assert( value != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_icmpv6( frame ) ) {
warn( "A non-icmpv6 packet (%#x) found while setting the target link-layer address.", info->format );
return true;
}
if ( info->icmpv6_type != ICMPV6_TYPE_NEIGHBOR_ADV ) {
warn( "Unsupported icmpv6 type (%#x) found while setting the target link-layer address.", info->icmpv6_type );
return true;
}
icmpv6_header_t *header = info->l3_payload;
icmpv6data_ndp_t *icmpv6data_ndp = ( icmpv6data_ndp_t * ) header->data;
if ( icmpv6data_ndp->ll_type != ICMPV6_ND_TARGET_LINK_LAYER_ADDRESS ) {
warn( "Incorrect link-layer address type (%#x) found while setting the target link-layer address.",
icmpv6data_ndp->ll_type );
return true;
}
set_dl_address( icmpv6data_ndp->ll_addr, value );
set_icmpv6_checksum( info->l3_header, header, info->l4_payload_length );
return parse_frame( frame );
}
static bool
set_mpls_label( buffer *frame, uint32_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_mpls( frame ) ) {
warn( "A non-mpls packet (%#x) found while setting the mpls label.", info->format );
return true;
}
mpls_header_t *mpls_header = info->l2_mpls_header;
mpls_header->label = ( mpls_header->label & htonl( 0x00000fff ) ) | htonl( ( value << 12 ) & 0xfffff000 );
return parse_frame( frame );
}
static bool
set_mpls_tc( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_mpls( frame ) ) {
warn( "A non-mpls packet (%#x) found while setting the traffic class.", info->format );
return true;
}
mpls_header_t *mpls_header = info->l2_mpls_header;
mpls_header->label = ( mpls_header->label & htonl( 0xfffff1ff ) ) | htonl( ( ( uint32_t ) value << 9 ) & 0x00000e00 );
return parse_frame( frame );
}
static bool
set_mpls_bos( buffer *frame, uint8_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_mpls( frame ) ) {
warn( "A non-mpls packet (%#x) found while setting the bottom of stack bit.", info->format );
return true;
}
mpls_header_t *mpls_header = info->l2_mpls_header;
mpls_header->label = ( mpls_header->label & htonl( 0xfffffeff ) ) | htonl( ( ( uint32_t ) value << 8 ) & 0x00000100 );
return parse_frame( frame );
}
static bool
set_pbb_isid( buffer *frame, uint32_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( info->l2_pbb_header == NULL ) {
warn( "A non-pbb packet (%#x) found while setting pbb sid.", info->format );
return true;
}
pbb_header_t *pbb_header = info->l2_pbb_header;
pbb_header->isid = ( pbb_header->isid & htonl( 0xFF000000 ) ) | htonl( value & 0x00FFFFFF );
return parse_frame( frame );
}
static bool
set_tunnel_id( buffer *frame, uint64_t value ) {
assert( frame != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
info->tunnel_id = value;
return true;
}
static void*
push_linklayer_tag( buffer *frame, void *head, size_t tag_size ) {
assert( frame != NULL );
assert( head != NULL );
size_t insert_offset = ( size_t ) ( ( char * ) head - ( char * ) frame->data );
append_back_buffer( frame, tag_size );
// head would be moved because append_back_buffer() may reallocate memory
head = ( char * ) frame->data + insert_offset;
memmove( ( char * ) head + tag_size, head, frame->length - insert_offset - tag_size );
memset( head, 0, tag_size );
return head;
}
static void
pop_linklayer_tag( buffer *frame, void *head, size_t tag_size ) {
assert( frame != NULL );
assert( head != NULL );
char *tail = ( char * ) head + tag_size;
size_t length = frame->length - ( ( size_t )( ( char * ) head - ( char * ) frame->data ) + tag_size );
memmove( head, tail, length );
frame->length -= tag_size;
}
static void*
push_vlan_tag( buffer *frame, void *head ) {
assert( frame != NULL );
assert( head != NULL );
return push_linklayer_tag( frame, head, sizeof( vlantag_header_t ) );
}
static void
pop_vlan_tag( buffer *frame, void *head ) {
assert( frame != NULL );
assert( head != NULL );
pop_linklayer_tag( frame, head, sizeof( vlantag_header_t ) );
}
static void*
push_mpls_tag( buffer *frame, void *head ) {
assert( frame != NULL );
assert( head != NULL );
return push_linklayer_tag( frame, head, sizeof( uint32_t ) );
}
static void
pop_mpls_tag( buffer *frame, void *head ) {
assert( frame != NULL );
assert( head != NULL );
pop_linklayer_tag( frame, head, sizeof( uint32_t ) );
}
static void*
push_pbb_tag( buffer *frame, void *head ) {
assert( frame != NULL );
assert( head != NULL );
return push_linklayer_tag( frame, head, sizeof( pbb_header_t ) );
}
static void
pop_pbb_tag( buffer *frame, void *head ) {
assert( frame != NULL );
assert( head != NULL );
pop_linklayer_tag( frame, head, sizeof( pbb_header_t ) );
}
static bool
decrement_ttl( uint8_t *ttl ) {
assert( ttl != NULL );
if ( *ttl == 0 ) {
return false;
}
( *ttl )--;
return true;
}
static bool
execute_action_copy_ttl_in( buffer *frame, action *copy_ttl_in ) {
assert( frame != NULL );
assert( copy_ttl_in != NULL );
packet_info *info = get_packet_info_data( frame );
if ( packet_type_eth_mpls( frame ) ) {
void *ptr = ( char * ) info->l2_mpls_header + sizeof( mpls_header_t );
size_t length = REMAINED_BUFFER_LENGTH( frame, ptr );
assert( info->l2_mpls_header != NULL );
mpls_header_t *mpls_header = info->l2_mpls_header;
uint32_t mpls = ntohl( mpls_header->label );
uint8_t mpls_bos = ( uint8_t ) ( ( mpls & 0x00000100 ) >> 8 );
uint8_t ttl = mpls & 0x000000FF;
if ( mpls_bos == 1 ) { // MPLS-to-IP copy
if ( length < sizeof( ipv4_header_t ) ) {
return false;
}
ipv4_header_t *ipv4_header = ptr;
// Inner payload MAY BE IPv4 or IPv6, below checks the first byte for version.
if ( ipv4_header->version == 4 ) {
if ( ipv4_header->ihl < 5 ) {
return false;
}
if ( length < ( size_t ) ipv4_header->ihl * 4 ) {
return false;
}
ipv4_header->ttl = ttl;
// no tcp/udp/icmp checksum caculation here because tos field is not included in pseudo header
set_ipv4_checksum( ipv4_header );
}
else if ( ipv4_header->version == 6 ) {
if ( length < sizeof( ipv6_header_t ) ) {
return false;
}
ipv6_header_t *ipv6_header = ptr;
ipv6_header->hoplimit = ttl;
}
else {
warn( "MPLS inner payload was not ipv4 or ipv6 (%#x) while setting the ttl field.", info->format );
return true;
}
}
else { // MPLS-to-MPLS copy
if ( length < sizeof( mpls_header_t ) ) {
debug("incomplete mpls");
return false;
}
mpls_header_t *inner_header = ( mpls_header_t * ) ( mpls_header + 1 );
//uint32_t inner_mpls = ntohl( inner_header->label ); // unused variable
uint8_t *inner_mpls_ttl = ( uint8_t * ) inner_header + 3;
*inner_mpls_ttl = ttl;
}
}
else if ( packet_type_ipv4( frame ) || packet_type_ipv6( frame ) ) {
warn( "IP-to-IP TTL copy not supported yet." );
}
else {
warn( "A non-ip,mpls packet (%#x) found while setting the ttl field.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
execute_action_pop_mpls( buffer *frame, action *pop_mpls ) {
assert( frame != NULL );
assert( pop_mpls != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_mpls( frame ) ) {
warn( "A non-mpls packet (%#x) found while retrieving the mpls label.", info->format );
return true;
}
pop_mpls_tag( frame, info->l2_mpls_header );
* ( uint16_t * ) ( ( char * ) info->l2_mpls_header - 2 ) = htons( pop_mpls->ethertype );
return parse_frame( frame );
}
static bool
execute_action_pop_vlan( buffer *frame, action *pop_vlan ) {
assert( frame != NULL );
assert( pop_vlan != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_vtag( frame ) ) {
warn( "A non-vlan frame (%#x) found while popping a vlan tag.", info->format );
return true;
}
pop_vlan_tag( frame, ( char * ) info->l2_vlan_header - 2 ); // remove TPID ethertype and tci
return parse_frame( frame );
}
static bool
execute_action_push_mpls( buffer *frame, action *push_mpls ) {
assert( frame != NULL );
assert( push_mpls != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
void *start = info->l2_payload;
uint32_t default_mpls = htonl( 0x00000100 );
if ( info->l2_mpls_header != NULL ) {
start = info->l2_mpls_header;
default_mpls = htonl( 0xFFFFFEFF ) & *( uint32_t * ) info->l2_mpls_header;
}
else if ( packet_type_ipv4( frame ) ) {
ipv4_header_t *ipv4_header = info->l3_header;
default_mpls = htonl( 0x00000100 | ( uint32_t ) ipv4_header->ttl );
}
else if ( packet_type_ipv6( frame ) ) {
ipv6_header_t *ipv6_header = info->l3_header;
default_mpls = htonl( 0x00000100 | ( uint32_t ) ipv6_header->hoplimit );
}
void *mpls = push_mpls_tag( frame, start );
* ( uint16_t * )( ( char * ) mpls - 2 ) = htons( push_mpls->ethertype );
mpls_header_t *mpls_header = mpls;
mpls_header->label = default_mpls;
return parse_frame( frame );
}
static bool
execute_action_push_vlan( buffer *frame, action *push_vlan ) {
assert( frame != NULL );
assert( push_vlan != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
char *start = info->l2_payload;
uint16_t default_tci = 0;
if ( info->l2_vlan_header != NULL ) {
start = info->l2_vlan_header;
default_tci = ( ( vlantag_header_t * ) ( info->l2_vlan_header ) )-> tci;
}
void *vlan = push_vlan_tag( frame, start - 2 ); // push vlan tag between source mac and ethertype
ether_header_t *ether_header = ( ether_header_t * ) frame->data;
ether_header->type = htons( push_vlan->ethertype );
( ( vlantag_header_t * )( ( char * ) vlan + 2 ) )->tci = default_tci;
return parse_frame( frame );
}
static bool
execute_action_copy_ttl_out( buffer *frame, action *copy_ttl_out ) {
assert( frame != NULL );
assert( copy_ttl_out != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( packet_type_eth_mpls( frame ) ) {
void *ptr = ( char * ) info->l2_mpls_header + sizeof( mpls_header_t );
size_t length = REMAINED_BUFFER_LENGTH( frame, ptr );
assert( info->l2_mpls_header != NULL );
mpls_header_t *mpls_header = info->l2_mpls_header;
uint32_t mpls = ntohl( mpls_header->label );
uint8_t mpls_bos = ( uint8_t ) ( ( mpls & 0x00000100 ) >> 8 );
uint8_t ttl = 0;
if ( mpls_bos == 1 ) { // IP-to-MPLS copy
if ( length < sizeof( ipv4_header_t ) ) {
return false;
}
ipv4_header_t *ipv4_header = ptr;
// Inner payload MAY BE IPv4 or IPv6, below checks the first byte for version.
if ( ipv4_header->version == 4 ) {
if ( ipv4_header->ihl < 5 ) {
return false;
}
if ( length < ( size_t ) ipv4_header->ihl * 4 ) {
return false;
}
ttl = ipv4_header->ttl;
}
else if ( ipv4_header->version == 6 ) {
if ( length < sizeof( ipv6_header_t ) ) {
return false;
}
ipv6_header_t *ipv6_header = ptr;
ttl = ipv6_header->hoplimit;
}
else {
warn( "MPLS inner payload was not ipv4 or ipv6 (%#x) while setting the ttl field.", info->format );
return true;
}
}
else { // MPLS-to-MPLS copy
if ( length < sizeof( mpls_header_t ) ) {
debug("incomplete mpls");
return false;
}
mpls_header_t *inner_header = ( mpls_header_t * ) ( mpls_header + 1 );
uint32_t inner_mpls = ntohl( inner_header->label );
ttl = inner_mpls & 0x000000FF;
}
uint8_t *mpls_ttl = ( ( uint8_t * ) info->l2_mpls_header ) + 3;
*mpls_ttl = ttl;
}
else if ( packet_type_ipv4( frame ) || packet_type_ipv6( frame ) ) {
warn( "IP-IP TTL copy not supported yet." );
}
else {
warn( "A non-ip,mpls packet (%#x) found while setting the ttl field.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
execute_action_dec_mpls_ttl( buffer *frame, action *dec_mpls_ttl ) {
assert( frame != NULL );
assert( dec_mpls_ttl != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_mpls( frame ) ) {
warn( "A non-mpls packet (%#x) found while decrementing the mpls ttl.", info->format );
return true;
}
assert( info->l2_mpls_header != NULL );
uint8_t *ttl = ( uint8_t * ) info->l2_mpls_header + 3;
if ( !decrement_ttl( ttl ) ) {
match *match = duplicate_match( dec_mpls_ttl->entry->match );
packet_info *info = ( packet_info * ) frame->user_data;
match->in_port.value = info->eth_in_port;
match->in_port.valid = true;
if ( info->eth_in_phy_port != match->in_port.value ) {
match->in_phy_port.value = info->eth_in_phy_port;
match->in_phy_port.valid = true;
}
if ( info->metadata != 0 ) {
match->metadata.value = info->metadata;
match->metadata.valid = true;
}
if ( info->tunnel_id != 0 ) {
match->tunnel_id.value = info->tunnel_id;
match->tunnel_id.valid = true;
}
notify_packet_in( OFPR_INVALID_TTL, dec_mpls_ttl->entry->table_id, dec_mpls_ttl->entry->cookie, match, frame, MISS_SEND_LEN );
delete_match( match );
}
return parse_frame( frame );
}
static bool
execute_action_dec_nw_ttl( buffer *frame, action *dec_nw_ttl ) {
assert( frame != NULL );
assert( dec_nw_ttl != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
bool ttl_exceeded = false;
uint8_t *ttl = NULL;
if ( packet_type_ipv4( frame ) ) {
ipv4_header_t *header = info->l3_header;
ttl = &header->ttl;
ttl_exceeded = !decrement_ttl( ttl );
// no tcp/udp/icmp checksum caculation here because ttl field is not included in pseudo header
set_ipv4_checksum( header );
}
else if ( packet_type_ipv6( frame ) ) {
ipv6_header_t *header = info->l3_header;
ttl = &header->hoplimit;
ttl_exceeded = !decrement_ttl( ttl );
}
else {
warn( "A non-ip packet (%#x) found while decrementing the ttl field.", info->format );
return true;
}
if ( ttl_exceeded ) {
match *match = duplicate_match( dec_nw_ttl->entry->match );
packet_info *info = ( packet_info * ) frame->user_data;
match->in_port.value = info->eth_in_port;
match->in_port.valid = true;
if ( info->eth_in_phy_port != match->in_port.value ) {
match->in_phy_port.value = info->eth_in_phy_port;
match->in_phy_port.valid = true;
}
if ( info->metadata != 0 ) {
match->metadata.value = info->metadata;
match->metadata.valid = true;
}
if ( info->tunnel_id != 0 ) {
match->tunnel_id.value = info->tunnel_id;
match->tunnel_id.valid = true;
}
notify_packet_in( OFPR_INVALID_TTL, dec_nw_ttl->entry->table_id, dec_nw_ttl->entry->cookie, match, frame, MISS_SEND_LEN );
delete_match( match );
}
return parse_frame( frame );
}
bool
execute_action_set_mpls_ttl( buffer *frame, action *set_mpls_ttl ) {
assert( frame != NULL );
assert( set_mpls_ttl != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( !packet_type_eth_mpls( frame ) ) {
warn( "A non-mpls packet (%#x) found while setting the mpls ttl.", info->format );
return true;
}
assert( info->l2_mpls_header != NULL );
uint8_t *ttl = ( uint8_t * ) info->l2_mpls_header + 3;
*ttl = set_mpls_ttl->mpls_ttl;
return parse_frame( frame );
}
static bool
execute_action_set_nw_ttl( buffer *frame, action *set_nw_ttl ) {
assert( frame != NULL );
assert( set_nw_ttl != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( packet_type_ipv4( frame ) ) {
ipv4_header_t *header = info->l3_header;
header->ttl = set_nw_ttl->nw_ttl;
set_ipv4_checksum( header );
}
else if ( packet_type_ipv6( frame ) ) {
ipv6_header_t *header = info->l3_header;
header->hoplimit = set_nw_ttl->nw_ttl;
}
else {
warn( "A non-ip packet (%#x) found while setting the ttl field.", info->format );
return true;
}
return parse_frame( frame );
}
static bool
execute_action_set_field( buffer *frame, action *set_field ) {
assert( frame != NULL );
assert( set_field != NULL );
assert( set_field->match != NULL );
match *match = set_field->match;
if ( match->eth_dst[ 0 ].valid ) {
if ( !set_dl_dst( frame, match->eth_dst ) ) {
return false;
}
}
if ( match->eth_src[ 0 ].valid ) {
if ( !set_dl_src( frame, match->eth_src ) ) {
return false;
}
}
if ( match->eth_type.valid ) {
if ( !set_dl_type( frame, match->eth_type.value ) ) {
return false;
}
}
if ( match->vlan_vid.valid ) {
if ( !set_vlan_vid( frame, match->vlan_vid.value ) ) {
return false;
}
}
if ( match->vlan_pcp.valid ) {
if ( !set_vlan_pcp( frame, match->vlan_pcp.value ) ) {
return false;
}
}
if ( match->ip_dscp.valid ) {
if ( !set_nw_dscp( frame, match->ip_dscp.value ) ) {
return false;
}
}
if ( match->ip_ecn.valid ) {
if ( !set_nw_ecn( frame, match->ip_ecn.value ) ) {
return false;
}
}
if ( match->ip_proto.valid ) {
if ( !set_ip_proto( frame, match->ip_proto.value ) ) {
return false;
}
}
if ( match->ipv4_src.valid ) {
if ( !set_ipv4_src( frame, match->ipv4_src.value ) ) {
return false;
}
}
if ( match->ipv4_dst.valid ) {
if ( !set_ipv4_dst( frame, match->ipv4_dst.value ) ) {
return false;
}
}
if ( match->tcp_src.valid ) {
if ( !set_tcp_src( frame, match->tcp_src.value ) ) {
return false;
}
}
if ( match->tcp_dst.valid ) {
if ( !set_tcp_dst( frame, match->tcp_dst.value ) ) {
return false;
}
}
if ( match->udp_src.valid ) {
if ( !set_udp_src( frame, match->udp_src.value ) ) {
return false;
}
}
if ( match->udp_dst.valid ) {
if ( !set_udp_dst( frame, match->udp_dst.value ) ) {
return false;
}
}
if ( match->sctp_src.valid ) {
if ( !set_sctp_src( frame, match->sctp_src.value ) ) {
return false;
}
}
if ( match->sctp_dst.valid ) {
if ( !set_sctp_dst( frame, match->sctp_dst.value ) ) {
return false;
}
}
if ( match->icmpv4_type.valid ) {
if ( !set_icmpv4_type( frame, match->icmpv4_type.value ) ) {
return false;
}
}
if ( match->icmpv4_code.valid ) {
if ( !set_icmpv4_code( frame, match->icmpv4_code.value ) ) {
return false;
}
}
if ( match->arp_op.valid ) {
if ( !set_arp_op( frame, match->arp_op.value ) ) {
return false;
}
}
if ( match->arp_spa.valid ) {
if ( !set_arp_spa( frame, match->arp_spa.value ) ) {
return false;
}
}
if ( match->arp_tpa.valid ) {
if ( !set_arp_tpa( frame, match->arp_tpa.value ) ) {
return false;
}
}
if ( match->arp_sha[ 0 ].valid ) {
if ( !set_arp_sha( frame, &match->arp_sha[ 0 ] ) ) {
return false;
}
}
if ( match->arp_tha[ 0 ].valid ) {
if ( !set_arp_tha( frame, &match->arp_tha[ 0 ] ) ) {
return false;
}
}
if ( match->ipv6_src[ 0 ].valid ) {
if ( !set_ipv6_src( frame, &match->ipv6_src[ 0 ] ) ) {
return false;
}
}
if ( match->ipv6_dst[ 0 ].valid ) {
if ( !set_ipv6_dst( frame, &match->ipv6_dst[ 0 ] ) ) {
return false;
}
}
if ( match->ipv6_flabel.valid ) {
if ( !set_ipv6_flabel( frame, match->ipv6_flabel.value ) ) {
return false;
}
}
if ( match->icmpv6_type.valid ) {
if ( !set_icmpv6_type( frame, match->icmpv6_type.value ) ) {
return false;
}
}
if ( match->icmpv6_code.valid ) {
if ( !set_icmpv6_code( frame, match->icmpv6_code.value ) ) {
return false;
}
}
if ( match->ipv6_nd_target[ 0 ].valid ) {
if ( !set_ipv6_nd_target( frame, match->ipv6_nd_target ) ) {
return false;
}
}
if ( match->ipv6_nd_sll[ 0 ].valid ) {
if ( !set_ipv6_nd_sll( frame, match->ipv6_nd_sll ) ) {
return false;
}
}
if ( match->ipv6_nd_tll[ 0 ].valid ) {
if ( !set_ipv6_nd_tll( frame, match->ipv6_nd_tll ) ) {
return false;
}
}
if ( match->mpls_label.valid ) {
if ( !set_mpls_label( frame, match->mpls_label.value ) ) {
return false;
}
}
if ( match->mpls_tc.valid ) {
if ( !set_mpls_tc( frame, match->mpls_tc.value ) ) {
return false;
}
}
if ( match->mpls_bos.valid ) {
if ( !set_mpls_bos( frame, match->mpls_bos.value ) ) {
return false;
}
}
if ( match->pbb_isid.valid ) {
if ( !set_pbb_isid( frame, match->pbb_isid.value ) ) {
return false;
}
}
if ( match->tunnel_id.valid ) {
if ( !set_tunnel_id( frame, match->tunnel_id.value ) ) {
return false;
}
}
return true;
}
static bool
execute_group_all( buffer *frame, bucket_list *buckets ) {
assert( frame != NULL );
assert( buckets != NULL );
bucket_list *bucket_element = get_first_element( buckets );
while ( bucket_element != NULL ) {
bucket *b = bucket_element->data;
if ( b != NULL ) {
b->packet_count++;
b->byte_count += frame->length;
if ( execute_action_list( b->actions, frame ) != OFDPE_SUCCESS ) {
return false;
}
}
bucket_element = bucket_element->next;
}
return true;
}
static bool
execute_action_push_pbb( buffer *frame, action *push_pbb ) {
assert( frame != NULL );
assert( push_pbb != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
char *start = info->l2_payload;
uint32_t default_isid = 0;
if ( info->l2_pbb_header != NULL ) {
// PBB I-SID <- PBB I-SID
start = info->l2_pbb_header;
pbb_header_t *pbb = info->l2_pbb_header;
default_isid |= ntohl( pbb->isid ) & 0x00FFFFFF;
}
if ( info->l2_vlan_header != NULL ) {
// PBB I-PCP <- VLAN PCP
vlantag_header_t *vlan = info->l2_vlan_header;
default_isid |= ( ( ( uint32_t ) ntohs( vlan->tci ) >> 13 ) << 29 ) & 0xE0000000;
}
void *new = push_pbb_tag( frame, start - 2 ); // push pbb before ethertype
*( ( uint16_t * ) new ) = htons( push_pbb->ethertype );
ether_header_t *ether = ( ether_header_t * ) frame->data;
pbb_header_t *pbb = ( pbb_header_t * ) ( ( char * ) new + 2 );
pbb->isid = htonl( default_isid );
memcpy( pbb->cda, ether->macda, ETH_ADDRLEN );
memcpy( pbb->csa, ether->macsa, ETH_ADDRLEN );
return parse_frame( frame );
}
static bool
execute_action_pop_pbb( buffer *frame, action *pop_pbb ) {
assert( frame != NULL );
assert( pop_pbb != NULL );
packet_info *info = get_packet_info_data( frame );
assert( info != NULL );
if ( info->l2_pbb_header == NULL ) {
warn( "A non-pbb frame (%#x) found while popping a pbb tag.", info->format );
return true;
}
pbb_header_t *pbb_header = info->l2_pbb_header;
ether_header_t *ether_header = frame->data;
memcpy( ether_header->macda, pbb_header->cda, ETH_ADDRLEN );
memcpy( ether_header->macsa, pbb_header->csa, ETH_ADDRLEN );
pop_pbb_tag( frame, ( char * ) info->l2_pbb_header - 2 ); // remove PBB ethertype and I-TAG
return parse_frame( frame );
}
static bool
check_bucket( action_list *actions ) {
assert( actions != NULL );
action_list *element = get_first_element( actions );
bool ret = true;
while ( element != NULL ) {
action *action = element->data;
if ( action != NULL ) {
if ( action->type == OFPAT_OUTPUT ) {
if ( !switch_port_is_up( action->port ) ) {
ret = false;
break;
}
}
}
element = element->next;
}
return ret;
}
#ifdef GROUP_SELECT_BY_HASH
static inline uint32_t
group_select_by_hash_core( uint32_t value, const void *key, int size ) {
// 32 bit FNV_prime
const uint32_t prime = 0x01000193UL;
const unsigned char *c = key;
for ( int i = 0; i < size; i++ ) {
value ^= ( const uint32_t ) c[ i ];
value *= prime;
}
return value;
}
static uint32_t
group_select_by_hash( const buffer *frame ) {
assert( frame != NULL );
assert( frame->user_data != NULL );
const packet_info *info = ( packet_info * ) frame->user_data;
if ( ( info->format & ( ETH_DIX | ETH_8023_SNAP ) ) == 0 ) {
return ( uint32_t ) rand();
}
// 32 bit offset_basis
uint32_t value = 0x811c9dc5UL;
value = group_select_by_hash_core( value, info->eth_macda, sizeof( info->eth_macda ) );
value = group_select_by_hash_core( value, info->eth_macsa, sizeof( info->eth_macsa ) );
value = group_select_by_hash_core( value, &info->eth_type, sizeof( info->eth_type ) );
if ( ( info->format & ETH_8021Q ) == ETH_8021Q ) {
value = group_select_by_hash_core( value, &info->vlan_vid, sizeof( info->vlan_vid ) );
}
if ( ( info->format & MPLS ) == MPLS ) {
value = group_select_by_hash_core( value, &info->mpls_label, sizeof( info->mpls_label ) );
}
if ( ( info->format & NW_IPV4 ) == NW_IPV4 ) {
value = group_select_by_hash_core( value, &info->ipv4_protocol, sizeof( info->ipv4_protocol ) );
value = group_select_by_hash_core( value, &info->ipv4_saddr, sizeof( info->ipv4_saddr ) );
value = group_select_by_hash_core( value, &info->ipv4_daddr, sizeof( info->ipv4_daddr ) );
}
else if ( ( info->format & NW_IPV6 ) == NW_IPV6 ) {
value = group_select_by_hash_core( value, &info->ipv6_protocol, sizeof( info->ipv6_protocol ) );
value = group_select_by_hash_core( value, &info->ipv6_saddr, sizeof( info->ipv6_saddr ) );
value = group_select_by_hash_core( value, &info->ipv6_daddr, sizeof( info->ipv6_daddr ) );
}
else {
return value;
}
if ( ( info->format & TP_TCP ) == TP_TCP ) {
value = group_select_by_hash_core( value, &info->tcp_src_port, sizeof( info->tcp_src_port ) );
value = group_select_by_hash_core( value, &info->tcp_dst_port, sizeof( info->tcp_dst_port ) );
}
else if ( ( info->format & TP_UDP ) == TP_UDP ) {
value = group_select_by_hash_core( value, &info->udp_src_port, sizeof( info->udp_src_port ) );
value = group_select_by_hash_core( value, &info->udp_dst_port, sizeof( info->udp_dst_port ) );
}
return value;
}
#endif
static bool
execute_group_select( buffer *frame, bucket_list *buckets ) {
assert( frame != NULL );
assert( buckets != NULL );
list_element *candidates = NULL;
create_list( &candidates );
uint32_t candidates_weight_total = 0;
dlist_element *bucket_element = get_first_element( buckets );
while ( bucket_element != NULL ) {
bucket *b = bucket_element->data;
if ( b != NULL ) {
if ( !check_bucket( b->actions ) ) {
continue;
}
candidates_weight_total += b->weight;
append_to_tail( &candidates, bucket_element );
}
bucket_element = bucket_element->next;
}
uint32_t length_of_candidates = list_length_of( candidates );
if ( length_of_candidates == 0 || candidates_weight_total == 0 ) {
delete_list( candidates );
return true;
}
#ifdef GROUP_SELECT_BY_HASH
uint32_t candidate_weight = group_select_by_hash( frame ) % candidates_weight_total;
#else
uint32_t candidate_weight = ( ( uint32_t ) rand() ) % candidates_weight_total;
#endif
uint32_t candidate_index = 0;
list_element *target = candidates;
for ( uint32_t i = 0; target != NULL && i < length_of_candidates; i++ ) {
bucket *b = target->data;
if ( candidate_weight < b->weight ) {
break;
}
candidate_index++;
candidate_weight -= b->weight;
target = target->next;
}
debug( "execute group select. bucket=%u(/%u)", candidate_index, length_of_candidates );
if ( target != NULL ) {
bucket_element = target->data;
bucket *b = bucket_element->data;
if ( b != NULL ) {
b->packet_count++;
b->byte_count += frame->length;
if ( execute_action_list( b->actions, frame ) != OFDPE_SUCCESS ) {
delete_list( candidates );
return false;
}
}
}
delete_list( candidates );
return true;
}
static bool
execute_group_indirect( buffer *frame, bucket_list *buckets ) {
assert( frame != NULL );
assert( buckets != NULL );
dlist_element *element = get_first_element( buckets );
if ( element->next != NULL || element->prev != NULL ) {
error( "Only a single bucket can exist in a group." );
return false;
}
bucket *b = element->data;
b->packet_count++;
b->byte_count += frame->length;
dlist_element *actions = get_first_element( b->actions );
if ( execute_action_list( actions, frame ) != OFDPE_SUCCESS ) {
return false;
}
return true;
}
static bool
execute_action_group( buffer *frame, action *group ) {
assert( frame != NULL );
assert( group != NULL );
group_entry *entry = lookup_group_entry( group->group_id );
if ( entry == NULL ) {
return true;
}
entry->packet_count++;
entry->byte_count += frame->length;
bool ret = false;
switch ( entry->type ) {
case OFPGT_ALL:
{
debug( "Executing action group (OFPGT_ALL)." );
ret = execute_group_all( frame, entry->buckets );
}
break;
case OFPGT_SELECT:
{
debug( "Execute action group (OFPGT_SELECT)." );
ret = execute_group_select( frame, entry->buckets );
}
break;
case OFPGT_INDIRECT:
{
debug( "Executing action group (OFPGT_INDIRECT)." );
ret = execute_group_indirect( frame, entry->buckets );
}
break;
case OFPGT_FF:
{
debug( "Executing action group (OFPGT_FF)." );
warn( "OFPGT_FF is not implemented." );
ret = false;
}
break;
default:
{
error( "Undefined group type (%#x).", entry->type );
ret = false;
}
break;
}
return ret;
}
static bool
execute_action_output( buffer *frame, action *output ) {
assert( frame != NULL );
assert( output != NULL );
bool ret = true;
if ( output->port == OFPP_CONTROLLER ) {
packet_info *info = ( packet_info * ) frame->user_data;
uint32_t in_port = info->eth_in_port;
switch_port *port = lookup_switch_port( in_port );
match *match = NULL;
uint8_t table_id = 0;
uint64_t cookie = 0;
if ( output->entry != NULL ) {
match = duplicate_match( output->entry->match );
cookie = output->entry->cookie;
table_id = output->entry->table_id;
} else {
match = create_match();
}
match->in_port.value = info->eth_in_port;
match->in_port.valid = true;
if ( info->eth_in_phy_port != match->in_port.value ) {
match->in_phy_port.value = info->eth_in_phy_port;
match->in_phy_port.valid = true;
}
if ( info->metadata != 0 ) {
match->metadata.value = info->metadata;
match->metadata.valid = true;
}
if ( info->tunnel_id != 0 ) {
match->tunnel_id.value = info->tunnel_id;
match->tunnel_id.valid = true;
}
if ( output->entry != NULL && output->entry->table_miss ) {
if ( port == NULL || ( port->config & OFPPC_NO_PACKET_IN ) == 0 ){
notify_packet_in( OFPR_NO_MATCH, table_id, cookie, match, frame, MISS_SEND_LEN );
}
}
else {
notify_packet_in( OFPR_ACTION, table_id, cookie, match, frame, output->max_len );
}
delete_match( match );
}
else if ( output->port == OFPP_TABLE ) {
packet_info *info = ( packet_info * ) frame->user_data;
uint32_t in_port = info->eth_in_port;
// port is valid standard switch port or OFPP_CONTROLLER
switch_port *port = lookup_switch_port( in_port );
bool free_port = false;
if ( port == NULL ){
port = ( switch_port * ) xmalloc( sizeof( switch_port ) );
memset( port, 0, sizeof( switch_port ) );
port->port_no = OFPP_CONTROLLER;
free_port = true;
}
handle_received_frame( port, frame );
if ( free_port ) {
xfree( port );
}
}
else {
if ( send_frame_from_switch_port( output->port, frame ) != OFDPE_SUCCESS ) {
ret = false;
}
}
return ret;
}
OFDPE
execute_action_list( action_list *list, buffer *frame ) {
assert( list != NULL );
assert( frame != NULL );
debug( "Executing action list ( list = %p, frame = %p ).", list, frame );
for ( action_list *element = get_first_element( list ); element != NULL; element = element->next ) {
action *action = element->data;
if ( action == NULL ) {
continue;
}
bool ret = false;
switch ( action->type ) {
case OFPAT_OUTPUT:
{
debug( "Executing action (OFPAT_OUTPUT): port = %u, maxlen = %u.", action->port, action->max_len );
ret = execute_action_output( frame, action );
}
break;
case OFPAT_COPY_TTL_OUT:
{
debug( "Executing action (OFPAT_COPY_TTL_OUT)." );
ret = execute_action_copy_ttl_out( frame, action );
}
break;
case OFPAT_COPY_TTL_IN:
{
debug( "Executing action (OFPAT_COPY_TTL_IN)." );
ret = execute_action_copy_ttl_in( frame, action );
}
break;
case OFPAT_SET_MPLS_TTL:
{
debug( "Executing action (OFPAT_SET_MPLS_TTL): ttl = %u.", action->mpls_ttl );
ret = execute_action_set_mpls_ttl( frame, action );
}
break;
case OFPAT_DEC_MPLS_TTL:
{
debug( "Executing action (OFPAT_DEC_MPLS_TTL)." );
ret = execute_action_dec_mpls_ttl( frame, action );
}
break;
case OFPAT_PUSH_VLAN:
{
debug( "Executing action (OFPAT_PUSH_VLAN)." );
ret = execute_action_push_vlan( frame, action );
}
break;
case OFPAT_POP_VLAN:
{
debug( "Executing action (OFPAT_POP_VLAN)." );
ret = execute_action_pop_vlan( frame, action );
}
break;
case OFPAT_PUSH_MPLS:
{
debug( "Executing action (OFPAT_PUSH_MPLS)." );
ret = execute_action_push_mpls( frame, action );
}
break;
case OFPAT_POP_MPLS:
{
debug( "Executing action (OFPAT_POP_MPLS)." );
ret = execute_action_pop_mpls( frame, action );
}
break;
case OFPAT_SET_QUEUE:
{
debug( "Executing action (OFPAT_SET_QUEUE)." );
warn( "OFPAT_SET_QUEUE is not supported." );
ret = false;
}
break;
case OFPAT_GROUP:
{
debug( "Executing action (OFPAT_GROUP)." );
ret = execute_action_group( frame, action );
}
break;
case OFPAT_SET_NW_TTL:
{
debug( "Executing action (OFPAT_SET_NW_TTL): ttl = %u.", action->nw_ttl );
ret = execute_action_set_nw_ttl( frame, action );
}
break;
case OFPAT_DEC_NW_TTL:
{
debug( "Executing action (OFPAT_DEC_NW_TTL)." );
ret = execute_action_dec_nw_ttl( frame, action );
}
break;
case OFPAT_SET_FIELD:
{
debug( "Executing action (OFPAT_SET_FIELD)." );
ret = execute_action_set_field( frame, action );
}
break;
case OFPAT_PUSH_PBB:
{
debug( "Executing action (OFPAT_PUSH_PBB)." );
ret = execute_action_push_pbb( frame, action );
}
break;
case OFPAT_POP_PBB:
{
debug( "Executing action (OFPAT_POP_PBB)." );
ret = execute_action_pop_pbb( frame, action );
}
break;
case OFPAT_EXPERIMENTER:
{
debug( "Executing action (OFPAT_EXPERIMENTER)." );
warn( "OFPAT_EXPERIMENTER is not supported." );
ret = false;
}
break;
default:
{
error( "Undefined actions type (%#x).", action->type );
ret = false;
}
break;
}
if ( !ret ) {
return OFDPE_FAILED;
}
}
return OFDPE_SUCCESS;
}
OFDPE
execute_action_set( action_set *set, buffer *frame ) {
assert( set != NULL );
assert( frame != NULL );
debug( "Executing action set ( set = %p, frame = %p ).", set, frame );
if ( set->copy_ttl_in != NULL ) {
debug( "Executing action (OFPAT_COPY_TTL_IN)." );
if ( !execute_action_copy_ttl_in( frame, set->copy_ttl_in ) ) {
return OFDPE_FAILED;
}
}
if ( set->pop_vlan != NULL ) {
debug( "Executing action (OFPAT_POP_VLAN)." );
if ( !execute_action_pop_vlan( frame, set->pop_vlan ) ) {
return OFDPE_FAILED;
}
}
if ( set->pop_pbb != NULL ) {
debug( "Executing action (OFPAT_POP_PBB)." );
if ( !execute_action_pop_pbb( frame, set->pop_pbb ) ) {
return OFDPE_FAILED;
}
}
if ( set->pop_mpls != NULL ) {
debug( "Executing action (OFPAT_POP_MPLS)." );
if ( !execute_action_pop_mpls( frame, set->pop_mpls ) ) {
return OFDPE_FAILED;
}
}
if ( set->push_mpls != NULL ) {
debug( "Executing action (OFPAT_PUSH_MPLS)." );
if ( !execute_action_push_mpls( frame, set->push_mpls ) ) {
return OFDPE_FAILED;
}
}
if ( set->push_pbb != NULL ) {
debug( "Executing action (OFPAT_PUSH_PBB)." );
if ( !execute_action_push_pbb( frame, set->push_pbb ) ) {
return OFDPE_FAILED;
}
}
if ( set->push_vlan != NULL ) {
debug( "Executing action (OFPAT_PUSH_VLAN)." );
if ( !execute_action_push_vlan( frame, set->push_vlan ) ) {
return OFDPE_FAILED;
}
}
if ( set->copy_ttl_out != NULL ) {
debug( "Executing action (OFPAT_COPY_TTL_OUT)." );
if ( !execute_action_copy_ttl_out( frame, set->copy_ttl_out ) ) {
return OFDPE_FAILED;
}
}
if ( set->dec_mpls_ttl != NULL ) {
debug( "Executing action (OFPAT_DEC_MPLS_TTL)." );
if ( !execute_action_dec_mpls_ttl( frame, set->dec_mpls_ttl ) ) {
return OFDPE_FAILED;
}
}
if ( set->dec_nw_ttl != NULL ) {
debug( "Executing action (OFPAT_DEC_NW_TTL)." );
if ( !execute_action_dec_nw_ttl( frame, set->dec_nw_ttl ) ) {
return OFDPE_FAILED;
}
}
if ( set->set_mpls_ttl != NULL ) {
debug( "Executing action (OFPAT_SET_MPLS_TTL)." );
if ( !execute_action_set_mpls_ttl( frame, set->set_mpls_ttl ) ) {
return OFDPE_FAILED;
}
}
if ( set->set_nw_ttl != NULL ) {
debug( "Executing action (OFPAT_SET_NW_TTL)." );
if ( !execute_action_set_nw_ttl( frame, set->set_nw_ttl ) ) {
return OFDPE_FAILED;
}
}
if ( set->set_field != NULL ) {
debug( "Executing action (OFPAT_SET_FIELD)." );
if ( !execute_action_set_field( frame, set->set_field ) ) {
return OFDPE_FAILED;
}
}
if ( set->set_queue != NULL ) {
debug( "Executing action (OFPAT_SET_QUEUE)." );
warn( "OFPAT_SET_QUEUE is not supported" );
return OFDPE_FAILED;
}
if ( set->group != NULL ) {
debug( "Executing action (OFPAT_GROUP)." );
if ( !execute_action_group( frame, set->group ) ) {
return OFDPE_FAILED;
}
}
if ( set->group == NULL && set->output != NULL ) {
debug( "Executing action (OFPAT_OUTPUT)." );
if ( !execute_action_output( frame, set->output ) ) {
return OFDPE_FAILED;
}
}
return OFDPE_SUCCESS;
}
OFDPE
execute_packet_out( uint32_t buffer_id, uint32_t in_port, action_list *action_list, buffer *frame ) {
assert( action_list != NULL );
buffer *target = NULL;
if ( get_logging_level() >= LOG_DEBUG ) {
debug( "Handling Packet-Out ( buffer_id = %#x, in_port = %u, actions_list = %p, frame = %p ).",
buffer_id, in_port, action_list, frame );
dump_action_list( action_list, debug );
if ( frame != NULL ) {
dump_buffer( frame, debug );
}
}
if ( buffer_id != OFP_NO_BUFFER ) {
target = get_packet_from_packet_in_buffer( buffer_id );
if ( target == NULL ) {
error( "Failed to retrieve packet from packet buffer ( buffer_id = %#x ).", buffer_id );
return ERROR_OFDPE_BAD_REQUEST_BUFFER_UNKNOWN;
}
}
else {
if ( frame == NULL ) {
return ERROR_OFDPE_BAD_REQUEST_BAD_PACKET;
}
target = duplicate_buffer( frame );
}
if ( target->user_data == NULL ) {
if ( !parse_packet( target ) ) {
free_buffer( target );
return ERROR_OFDPE_BAD_REQUEST_BAD_PACKET;
}
}
assert( target->user_data != NULL );
if ( in_port > 0 && ( in_port <= OFPP_MAX || in_port == OFPP_CONTROLLER ) ) {
( ( packet_info * ) target->user_data )->eth_in_port = in_port;
( ( packet_info * ) target->user_data )->eth_in_phy_port = in_port;
}
if ( !lock_pipeline() ) {
free_buffer( target );
return OFDPE_FAILED;
}
OFDPE ret = execute_action_list( action_list, target );
if ( ret != OFDPE_SUCCESS ) {
error( "Failed to execute action list on Packet-Out ( action_list = %p, target = %p ).",
action_list, target );
dump_action_list( action_list, error );
dump_buffer( target, error );
}
unlock_pipeline();
free_buffer( target );
return ret;
}
/*
* Local variables:
* c-basic-offset: 2
* indent-tabs-mode: nil
* End:
*/