diff --git a/datapath/dp_act.c b/datapath/dp_act.c index 63df0d8..4a8f71b 100644 --- a/datapath/dp_act.c +++ b/datapath/dp_act.c @@ -195,6 +195,34 @@ set_nw_addr(struct sk_buff *skb, struct sw_flow_key *key, return skb; } +static struct sk_buff * +set_nw_tos(struct sk_buff *skb, struct sw_flow_key *key, + const struct ofp_action_header *ah) +{ + struct ofp_action_nw_tos *nt = (struct ofp_action_nw_tos *)ah; + uint16_t eth_proto = ntohs(key->dl_type); + + if (eth_proto == ETH_P_IP) { + struct iphdr *nh = ip_hdr(skb); + uint8_t new, *field; + + /* JeanII : Set only 6 bits, don't clobber ECN */ + new = (nt->nw_tos & 0xFC) | (nh->tos & 0x03); + + /* Get address of field */ + field = &nh->tos; + /* jklee : ip tos field is not included in TCP pseudo header. + * Need magic as update_csum() don't work with 8 bits. */ + update_csum(&nh->check, skb, htons((uint16_t)*field), + htons((uint16_t)new), 0); + + /* Update in packet */ + *field = new; + } + + return skb; +} + static struct sk_buff * set_tp_port(struct sk_buff *skb, struct sw_flow_key *key, const struct ofp_action_header *ah) @@ -293,6 +321,12 @@ static const struct openflow_action of_actions[] = { NULL, set_nw_addr }, + [OFPAT_SET_NW_TOS] = { + sizeof(struct ofp_action_nw_tos), + sizeof(struct ofp_action_nw_tos), + NULL, + set_nw_tos + }, [OFPAT_SET_TP_SRC] = { sizeof(struct ofp_action_tp_port), sizeof(struct ofp_action_tp_port), diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h index 5cc8529..72024d5 100644 --- a/include/openflow/openflow.h +++ b/include/openflow/openflow.h @@ -1,6 +1,6 @@ /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford * Junior University - * + * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute @@ -13,10 +13,10 @@ * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -25,7 +25,7 @@ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. - * + * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. @@ -61,7 +61,7 @@ #endif /* The most significant bit being set in the version field indicates an - * experimental OpenFlow version. + * experimental OpenFlow version. */ #define OFP_VERSION 0x97 @@ -79,18 +79,18 @@ enum ofp_port { OFPP_MAX = 0xff00, /* Fake output "ports". */ - OFPP_IN_PORT = 0xfff8, /* Send the packet out the input port. This - virtual port must be explicitly used - in order to send back out of the input + OFPP_IN_PORT = 0xfff8, /* Send the packet out the input port. This + virtual port must be explicitly used + in order to send back out of the input port. */ - OFPP_TABLE = 0xfff9, /* Perform actions in flow table. + OFPP_TABLE = 0xfff9, /* Perform actions in flow table. NB: This can only be the destination port for packet-out messages. */ OFPP_NORMAL = 0xfffa, /* Process with normal L2/L3 switching. */ - OFPP_FLOOD = 0xfffb, /* All physical ports except input port and + OFPP_FLOOD = 0xfffb, /* All physical ports except input port and those disabled by STP. */ OFPP_ALL = 0xfffc, /* All physical ports except input port. */ - OFPP_CONTROLLER = 0xfffd, /* Send to controller. */ + OFPP_CONTROLLER = 0xfffd, /* Send to controller. */ OFPP_LOCAL = 0xfffe, /* Local openflow "port". */ OFPP_NONE = 0xffff /* Not associated with a physical port. */ }; @@ -178,7 +178,7 @@ enum ofp_capabilities { /* Flags to indicate behavior of the physical port. These flags are * used in ofp_phy_port to describe the current configuration. They are - * used in the ofp_port_mod message to configure the port's behavior. + * used in the ofp_port_mod message to configure the port's behavior. */ enum ofp_port_config { OFPPC_PORT_DOWN = 1 << 0, /* Port is administratively down. */ @@ -286,15 +286,15 @@ struct ofp_port_mod { struct ofp_header header; uint16_t port_no; uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not - configurable. This is used to - sanity-check the request, so it must + configurable. This is used to + sanity-check the request, so it must be the same as returned in an ofp_phy_port struct. */ uint32_t config; /* Bitmap of OFPPC_* flags. */ uint32_t mask; /* Bitmap of OFPPC_* flags to be changed. */ - uint32_t advertise; /* Bitmap of "ofp_port_features"s. Zero all + uint32_t advertise; /* Bitmap of "ofp_port_features"s. Zero all bits to prevent any action taking place. */ uint8_t pad[4]; /* Pad to 64-bits. */ }; @@ -315,7 +315,7 @@ struct ofp_packet_in { uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ uint8_t pad; uint8_t data[0]; /* Ethernet frame, halfway through 32-bit word, - so the IP header is 32-bit aligned. The + so the IP header is 32-bit aligned. The amount of data is inferred from the length field in the header. Because of padding, offsetof(struct ofp_packet_in, data) == @@ -338,9 +338,9 @@ enum ofp_action_type { OFPAT_VENDOR = 0xffff }; -/* Action structure for OFPAT_OUTPUT, which sends packets out 'port'. - * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max - * number of bytes to send. A 'max_len' of zero means the entire packet +/* Action structure for OFPAT_OUTPUT, which sends packets out 'port'. + * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max + * number of bytes to send. A 'max_len' of zero means the entire packet * should be sent. */ struct ofp_action_output { uint16_t type; /* OFPAT_OUTPUT. */ @@ -406,25 +406,25 @@ struct ofp_action_nw_tos { uint8_t nw_tos; /* IP ToS/DSCP (6 bits). */ uint8_t pad[3]; }; -OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8); +OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8); /* Action header for OFPAT_VENDOR. The rest of the body is vendor-defined. */ struct ofp_action_vendor_header { uint16_t type; /* OFPAT_VENDOR. */ uint16_t len; /* Length is 8. */ - uint32_t vendor; /* Vendor ID, which takes the same form - as in "struct ofp_vendor_header". */ + uint32_t vendor; /* Vendor ID, which takes the same form + as in "struct ofp_vendor_header". */ }; OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8); /* Action header that is common to all actions. The length includes the - * header and any padding used to make the action 64-bit aligned. + * header and any padding used to make the action 64-bit aligned. * NB: The length of an action *must* always be a multiple of eight. */ struct ofp_action_header { uint16_t type; /* One of OFPAT_*. */ - uint16_t len; /* Length of action, including this - header. This is the length of action, - including any padding to make it + uint16_t len; /* Length of action, including this + header. This is the length of action, + including any padding to make it 64-bit aligned. */ uint8_t pad[4]; }; @@ -437,8 +437,8 @@ struct ofp_packet_out { uint16_t in_port; /* Packet's input port (OFPP_NONE if none). */ uint16_t actions_len; /* Size of action array in bytes. */ struct ofp_action_header actions[0]; /* Actions. */ - /* uint8_t data[0]; */ /* Packet data. The length is inferred - from the length field in the header. + /* uint8_t data[0]; */ /* Packet data. The length is inferred + from the length field in the header. (Only meaningful if buffer_id == -1.) */ }; OFP_ASSERT(sizeof(struct ofp_packet_out) == 16); @@ -483,7 +483,7 @@ enum ofp_flow_wildcards { OFPFW_ALL = ((1 << 21) - 1) }; -/* The wildcards for ICMP type and code fields use the transport source +/* The wildcards for ICMP type and code fields use the transport source * and destination port fields, respectively. */ #define OFPFW_ICMP_TYPE OFPFW_TP_SRC #define OFPFW_ICMP_CODE OFPFW_TP_DST @@ -521,7 +521,7 @@ struct ofp_match { }; OFP_ASSERT(sizeof(struct ofp_match) == 36); -/* The match fields for ICMP type and code use the transport source and +/* The match fields for ICMP type and code use the transport source and * destination port fields, respectively. */ #define icmp_type tp_src #define icmp_code tp_dst @@ -549,16 +549,16 @@ struct ofp_flow_mod { uint16_t idle_timeout; /* Idle time before discarding (seconds). */ uint16_t hard_timeout; /* Max time before discarding (seconds). */ uint16_t priority; /* Priority level of flow entry. */ - uint32_t buffer_id; /* Buffered packet to apply to (or -1). + uint32_t buffer_id; /* Buffered packet to apply to (or -1). Not meaningful for OFPFC_DELETE*. */ uint16_t out_port; /* For OFPFC_DELETE* commands, require - matching entries to include this as an - output port. A value of OFPP_NONE + matching entries to include this as an + output port. A value of OFPP_NONE indicates no restriction. */ uint16_t flags; /* One of OFPFF_*. */ uint32_t reserved; /* Reserved for future use. */ - struct ofp_action_header actions[0]; /* The action length is inferred - from the length field in the + struct ofp_action_header actions[0]; /* The action length is inferred + from the length field in the header. */ }; OFP_ASSERT(sizeof(struct ofp_flow_mod) == 64); @@ -582,7 +582,7 @@ struct ofp_flow_expired { uint32_t duration; /* Time flow was alive in seconds. */ uint16_t idle_timeout; /* Idle timeout from original flow mod. */ uint8_t pad2[2]; /* Align to 64-bits. */ - uint64_t packet_count; + uint64_t packet_count; uint64_t byte_count; }; OFP_ASSERT(sizeof(struct ofp_flow_expired) == 72); @@ -610,13 +610,13 @@ enum ofp_bad_request_code { OFPBRC_BAD_VERSION, /* ofp_header.version not supported. */ OFPBRC_BAD_TYPE, /* ofp_header.type not supported. */ OFPBRC_BAD_STAT, /* ofp_stats_request.type not supported. */ - OFPBRC_BAD_VENDOR, /* Vendor not supported (in ofp_vendor_header + OFPBRC_BAD_VENDOR, /* Vendor not supported (in ofp_vendor_header * or ofp_stats_request or ofp_stats_reply). */ OFPBRC_BAD_SUBTYPE, /* Vendor subtype not supported. */ OFPBRC_EPERM /* Permissions error. */ }; -/* ofp_error_msg 'code' values for OFPET_BAD_ACTION. 'data' contains at least +/* ofp_error_msg 'code' values for OFPET_BAD_ACTION. 'data' contains at least * the first 64 bytes of the failed request. */ enum ofp_bad_action_code { OFPBAC_BAD_TYPE, /* Unknown action type. */ @@ -628,11 +628,11 @@ enum ofp_bad_action_code { OFPBAC_EPERM /* Permissions error. */ }; -/* ofp_error_msg 'code' values for OFPET_FLOW_MOD_FAILED. 'data' contains +/* ofp_error_msg 'code' values for OFPET_FLOW_MOD_FAILED. 'data' contains * at least the first 64 bytes of the failed request. */ enum ofp_flow_mod_failed_code { OFPFMFC_ALL_TABLES_FULL, /* Flow not added because of full tables. */ - OFPFMFC_OVERLAP, /* Attempted to add overlapping flow with + OFPFMFC_OVERLAP, /* Attempted to add overlapping flow with * CHECK_OVERLAP flag set. */ OFPFMFC_EPERM /* Permissions error. */ }; @@ -643,13 +643,13 @@ struct ofp_error_msg { uint16_t type; uint16_t code; - uint8_t data[0]; /* Variable-length data. Interpreted based + uint8_t data[0]; /* Variable-length data. Interpreted based on the type and code. */ }; OFP_ASSERT(sizeof(struct ofp_error_msg) == 12); enum ofp_stats_types { - /* Description of this OpenFlow switch. + /* Description of this OpenFlow switch. * The request body is empty. * The reply body is struct ofp_desc_stats. */ OFPST_DESC, @@ -676,7 +676,7 @@ enum ofp_stats_types { /* Vendor extension. * The request and reply bodies begin with a 32-bit vendor ID, which takes - * the same form as in "struct ofp_vendor_header". The request and reply + * the same form as in "struct ofp_vendor_header". The request and reply * bodies are otherwise vendor-defined. */ OFPST_VENDOR = 0xffff }; @@ -703,7 +703,7 @@ OFP_ASSERT(sizeof(struct ofp_stats_reply) == 12); #define DESC_STR_LEN 256 #define SERIAL_NUM_LEN 32 -/* Body of reply to OFPST_DESC request. Each entry is a NULL-terminated +/* Body of reply to OFPST_DESC request. Each entry is a NULL-terminated * ASCII string. */ struct ofp_desc_stats { char mfr_desc[DESC_STR_LEN]; /* Manufacturer description. */ @@ -719,8 +719,8 @@ struct ofp_flow_stats_request { uint8_t table_id; /* ID of table to read (from ofp_table_stats), 0xff for all tables or 0xfe for emergency. */ uint8_t pad; /* Align to 32 bits. */ - uint16_t out_port; /* Require matching entries to include this - as an output port. A value of OFPP_NONE + uint16_t out_port; /* Require matching entries to include this + as an output port. A value of OFPP_NONE indicates no restriction. */ }; OFP_ASSERT(sizeof(struct ofp_flow_stats_request) == 40); @@ -749,8 +749,8 @@ struct ofp_aggregate_stats_request { uint8_t table_id; /* ID of table to read (from ofp_table_stats) 0xff for all tables or 0xfe for emergency. */ uint8_t pad; /* Align to 32 bits. */ - uint16_t out_port; /* Require matching entries to include this - as an output port. A value of OFPP_NONE + uint16_t out_port; /* Require matching entries to include this + as an output port. A value of OFPP_NONE indicates no restriction. */ }; OFP_ASSERT(sizeof(struct ofp_aggregate_stats_request) == 40); @@ -770,7 +770,7 @@ struct ofp_table_stats { are consulted first. */ uint8_t pad[3]; /* Align to 32-bits. */ char name[OFP_MAX_TABLE_NAME_LEN]; - uint32_t wildcards; /* Bitmap of OFPFW_* wildcards that are + uint32_t wildcards; /* Bitmap of OFPFW_* wildcards that are supported by the table. */ uint32_t max_entries; /* Max number of entries supported. */ uint32_t active_count; /* Number of active entries. */ @@ -788,20 +788,20 @@ struct ofp_port_stats { uint64_t tx_packets; /* Number of transmitted packets. */ uint64_t rx_bytes; /* Number of received bytes. */ uint64_t tx_bytes; /* Number of transmitted bytes. */ - uint64_t rx_dropped; /* Number of packets dropped by RX. */ - uint64_t tx_dropped; /* Number of packets dropped by TX. */ + uint64_t rx_dropped; /* Number of packets dropped by RX. */ + uint64_t tx_dropped; /* Number of packets dropped by TX. */ uint64_t rx_errors; /* Number of receive errors. This is a super-set of more specific receive errors and should be - greater than or equal to the sum of all + greater than or equal to the sum of all rx_*_err values. */ uint64_t tx_errors; /* Number of transmit errors. This is a super-set of more specific transmit errors and should be greater than or equal to the sum of all tx_*_err values (none currently defined.) */ - uint64_t rx_frame_err; /* Number of frame alignment errors. */ - uint64_t rx_over_err; /* Number of packets with RX overrun. */ - uint64_t rx_crc_err; /* Number of CRC errors. */ - uint64_t collisions; /* Number of collisions. */ + uint64_t rx_frame_err; /* Number of frame alignment errors. */ + uint64_t rx_over_err; /* Number of packets with RX overrun. */ + uint64_t rx_crc_err; /* Number of CRC errors. */ + uint64_t collisions; /* Number of collisions. */ }; OFP_ASSERT(sizeof(struct ofp_port_stats) == 104); diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 496f226..4cdb43b 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -263,6 +263,10 @@ ofp_print_action(struct ds *string, const struct ofp_action_header *ah, sizeof(struct ofp_action_nw_addr), sizeof(struct ofp_action_nw_addr), }, + [OFPAT_SET_NW_TOS] = { + sizeof(struct ofp_action_nw_tos), + sizeof(struct ofp_action_nw_tos), + }, [OFPAT_SET_TP_SRC] = { sizeof(struct ofp_action_tp_port), sizeof(struct ofp_action_tp_port), @@ -363,6 +367,12 @@ ofp_print_action(struct ds *string, const struct ofp_action_header *ah, break; } + case OFPAT_SET_NW_TOS: { + struct ofp_action_nw_tos *nt = (struct ofp_action_nw_tos *)ah; + ds_put_format(string, "mod_ip_tos:%d", nt->nw_tos); + break; + } + case OFPAT_SET_TP_SRC: { struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah; ds_put_format(string, "mod_tp_src:%d", ntohs(ta->tp_port)); diff --git a/regress/lib/Perl5/OF/OFUtil.pm b/regress/lib/Perl5/OF/OFUtil.pm index e6cdea2..aa09cac 100644 --- a/regress/lib/Perl5/OF/OFUtil.pm +++ b/regress/lib/Perl5/OF/OFUtil.pm @@ -271,20 +271,19 @@ sub setup_NF2 { sub setup_user { + setup_pcap_interfaces(); - setup_pcap_interfaces(); + # create openflow switch on four ports + my $if_string = ''; + for (my $i = 5 ; $i <= 7 ; $i++) { + $if_string .= nftest_get_iface("eth$i") . ','; + } + $if_string .= nftest_get_iface("eth8"); - # create openflow switch on four ports - my $if_string = ''; - for ( my $i = 5 ; $i <= 7 ; $i++ ) { - $if_string .= nftest_get_iface("eth$i") . ','; - } - $if_string .= nftest_get_iface("eth8"); + print "about to create ofdatapath` punix:/var/run/test -i $if_string \& \n"; + system("${openflow_dir}/udatapath/ofdatapath punix:/var/run/test -i $if_string \&"); - print "about to create ofdatapath` punix:/var/run/test -i $if_string \& \n"; - system("${openflow_dir}/udatapath/ofdatapath punix:/var/run/test -i $if_string \&"); - - start_ofprotocol("unix:/var/run/test", @_); + start_ofprotocol("unix:/var/run/test", @_); } sub teardown_kmod { @@ -815,168 +814,173 @@ sub create_flow_mod_from_udp { } sub flow_mod_length { - my ( $mod_type, $chg_field ) = @_; + my ($mod_type, $chg_field) = @_; - my $action_length; - if ($mod_type eq 'drop') { - $action_length = 0; - } elsif (defined $chg_field) { - if (($chg_field eq 'dl_src') || ($chg_field eq 'dl_dst')) { - $action_length = $ofp->sizeof('ofp_action_dl_addr') + - $ofp->sizeof('ofp_action_output'); - } elsif (($chg_field eq 'nw_src') || ($chg_field eq 'nw_dst')) { - $action_length = $ofp->sizeof('ofp_action_nw_addr') + - $ofp->sizeof('ofp_action_output'); - } elsif (($chg_field eq 'tp_src') || ($chg_field eq 'tp_dst')) { - $action_length = $ofp->sizeof('ofp_action_tp_port') + - $ofp->sizeof('ofp_action_output'); - } elsif ($chg_field eq 'strip_vlan') { - $action_length = $ofp->sizeof('ofp_action_header') + - $ofp->sizeof('ofp_action_output'); - } elsif ($chg_field eq 'vlan_vid') { - $action_length = $ofp->sizeof('ofp_action_vlan_vid') + - $ofp->sizeof('ofp_action_output'); - } elsif ($chg_field eq 'vlan_pcp') { - $action_length = $ofp->sizeof('ofp_action_vlan_pcp') + - $ofp->sizeof('ofp_action_output'); - } else { - $action_length = $ofp->sizeof('ofp_action_output'); - } + my $action_length = 0; + + if ($mod_type eq 'drop') { + $action_length = 0; + } elsif (defined $chg_field) { + if (($chg_field eq 'dl_src') || ($chg_field eq 'dl_dst')) { + $action_length = $ofp->sizeof('ofp_action_dl_addr') + + $ofp->sizeof('ofp_action_output'); + } elsif (($chg_field eq 'nw_src') || ($chg_field eq 'nw_dst')) { + $action_length = $ofp->sizeof('ofp_action_nw_addr') + + $ofp->sizeof('ofp_action_output'); + } elsif ($chg_field eq 'nw_tos') { + $action_length = $ofp->sizeof('ofp_action_nw_tos') + + $ofp->sizeof('ofp_action_output'); + } elsif (($chg_field eq 'tp_src') || ($chg_field eq 'tp_dst')) { + $action_length = $ofp->sizeof('ofp_action_tp_port') + + $ofp->sizeof('ofp_action_output'); + } elsif ($chg_field eq 'strip_vlan') { + $action_length = $ofp->sizeof('ofp_action_header') + + $ofp->sizeof('ofp_action_output'); + } elsif ($chg_field eq 'vlan_vid') { + $action_length = $ofp->sizeof('ofp_action_vlan_vid') + + $ofp->sizeof('ofp_action_output'); + } elsif ($chg_field eq 'vlan_pcp') { + $action_length = $ofp->sizeof('ofp_action_vlan_pcp') + + $ofp->sizeof('ofp_action_output'); } else { - $action_length = $ofp->sizeof('ofp_action_output'); + $action_length = $ofp->sizeof('ofp_action_output'); } - my $length = $ofp->sizeof('ofp_flow_mod') + $action_length; - return $length; + } else { + $action_length = $ofp->sizeof('ofp_action_output'); + } + + my $length = $ofp->sizeof('ofp_flow_mod') + $action_length; + return $length; } sub combine_args { - my ($flow_mod, $mod_type, $out_port, $chg_field, $chg_val) = @_; + my ($flow_mod, $mod_type, $out_port, $chg_field, $chg_val) = @_; - my @pad_6 = (0,0,0,0,0,0); - my @pad_4 = (0,0,0,0); - my @pad_3 = (0,0,0); - my @pad_2 = (0,0); + my @pad_6 = (0,0,0,0,0,0); + my @pad_4 = (0,0,0,0); + my @pad_3 = (0,0,0); + my @pad_2 = (0,0); - my $nw_addr_org; - my $ok_org; + my $nw_addr_org; + my $ok_org; - my @dl_addr_org; - my $chg_vlan_pcp_val; + my @dl_addr_org; + my $chg_vlan_pcp_val; - #OUTPUT - #and No action for drops - my $action_output_args; - my $action_output; - if ($mod_type ne 'drop') { - $action_output_args = { - type => $enums{'OFPAT_OUTPUT'}, - len => $ofp->sizeof('ofp_action_output'), - port => $out_port, - max_len => 0, # send entire packet - }; - $action_output = $ofp->pack( 'ofp_action_output', $action_output_args ); - } + #OUTPUT + #and No action for drops + my $action_output_args; + my $action_output; + if ($mod_type ne 'drop') { + $action_output_args = { + type => $enums{'OFPAT_OUTPUT'}, + len => $ofp->sizeof('ofp_action_output'), + port => $out_port, + max_len => 0, # send entire packet + }; + $action_output = $ofp->pack('ofp_action_output', $action_output_args); + } - #MODIFY ACTION - my $action_mod_args; - my $action_mod; - if (defined $chg_field) { - #SET_DL_SRC - if ($chg_field eq 'dl_src') { - @dl_addr_org = NF2::PDU::get_MAC_address($chg_val); - $action_mod_args = { - type => $enums{'OFPAT_SET_DL_SRC'}, - len => $ofp->sizeof('ofp_action_dl_addr'), - dl_addr => \@dl_addr_org, - pad => \@pad_6, - }; - $action_mod = $ofp->pack('ofp_action_dl_addr', $action_mod_args); - #SET_DL_DST - } elsif ($chg_field eq 'dl_dst') { - @dl_addr_org = NF2::PDU::get_MAC_address($chg_val); - $action_mod_args = { - type => $enums{'OFPAT_SET_DL_DST'}, - len => $ofp->sizeof('ofp_action_dl_addr'), - dl_addr => \@dl_addr_org, - pad => \@pad_6, - }; - $action_mod = $ofp->pack('ofp_action_dl_addr', $action_mod_args); - #SET_NW_SRC - } elsif ($chg_field eq 'nw_src') { - ($nw_addr_org, $ok_org) = NF2::IP_hdr::getIP($chg_val); - $action_mod_args = { - type => $enums{'OFPAT_SET_NW_SRC'}, - len => $ofp->sizeof('ofp_action_nw_addr'), - nw_addr => $nw_addr_org, - }; - $action_mod = $ofp->pack('ofp_action_nw_addr', $action_mod_args); - #SET_NW_DST - } elsif ($chg_field eq 'nw_dst') { - ($nw_addr_org, $ok_org) = NF2::IP_hdr::getIP($chg_val); - $action_mod_args = { - type => $enums{'OFPAT_SET_NW_DST'}, - len => $ofp->sizeof('ofp_action_nw_addr'), - nw_addr => $nw_addr_org, - }; - $action_mod = $ofp->pack('ofp_action_nw_addr', $action_mod_args); - #SET_TP_SRC - } elsif ($chg_field eq 'tp_src') { - $action_mod_args = { - type => $enums{'OFPAT_SET_TP_SRC'}, - len => $ofp->sizeof('ofp_action_tp_port'), - tp_port => $chg_val, - pad => \@pad_2, - }; - $action_mod = $ofp->pack('ofp_action_tp_port', $action_mod_args); - #SET_TP_DST - } elsif ($chg_field eq 'tp_dst') { - $action_mod_args = { - type => $enums{'OFPAT_SET_TP_DST'}, - len => $ofp->sizeof('ofp_action_tp_port'), - tp_port => $chg_val, - pad => \@pad_2, - }; - $action_mod = $ofp->pack('ofp_action_tp_port', $action_mod_args); - #STRIP_VLAN - } elsif ($chg_field eq 'strip_vlan') { - $action_mod_args = { - type => $enums{'OFPAT_STRIP_VLAN'}, - len => $ofp->sizeof('ofp_action_header'), - pad => \@pad_4, - }; - $action_mod = $ofp->pack('ofp_action_header', $action_mod_args); - #SET_VLAN_VID - } elsif ($chg_field eq 'vlan_vid') { - $action_mod_args = { - type => $enums{'OFPAT_SET_VLAN_VID'}, - len => $ofp->sizeof('ofp_action_vlan_vid'), - vlan_vid => $chg_val, - pad => \@pad_2, - }; - $action_mod = $ofp->pack('ofp_action_vlan_vid', $action_mod_args); - #SET_VLAN_PCP - } elsif ($chg_field eq 'vlan_pcp') { - $chg_vlan_pcp_val = ($chg_val>>13) & 0x0007; - $action_mod_args = { - type => $enums{'OFPAT_SET_VLAN_PCP'}, - len => $ofp->sizeof('ofp_action_vlan_pcp'), - vlan_pcp => $chg_vlan_pcp_val, - pad => \@pad_3, - }; - $action_mod = $ofp->pack('ofp_action_vlan_pcp', $action_mod_args); - } else { - $action_mod = undef; - } + #MODIFY ACTION + my $action_mod_args; + my $action_mod; + if (defined $chg_field) { + if ($chg_field eq 'dl_src') { #SET_DL_SRC + @dl_addr_org = NF2::PDU::get_MAC_address($chg_val); + $action_mod_args = { + type => $enums{'OFPAT_SET_DL_SRC'}, + len => $ofp->sizeof('ofp_action_dl_addr'), + dl_addr => \@dl_addr_org, + pad => \@pad_6, + }; + $action_mod = $ofp->pack('ofp_action_dl_addr', $action_mod_args); + } elsif ($chg_field eq 'dl_dst') { #SET_DL_DST + @dl_addr_org = NF2::PDU::get_MAC_address($chg_val); + $action_mod_args = { + type => $enums{'OFPAT_SET_DL_DST'}, + len => $ofp->sizeof('ofp_action_dl_addr'), + dl_addr => \@dl_addr_org, + pad => \@pad_6, + }; + $action_mod = $ofp->pack('ofp_action_dl_addr', $action_mod_args); + } elsif ($chg_field eq 'nw_src') { #SET_NW_SRC + ($nw_addr_org, $ok_org) = NF2::IP_hdr::getIP($chg_val); + $action_mod_args = { + type => $enums{'OFPAT_SET_NW_SRC'}, + len => $ofp->sizeof('ofp_action_nw_addr'), + nw_addr => $nw_addr_org, + }; + $action_mod = $ofp->pack('ofp_action_nw_addr', $action_mod_args); + } elsif ($chg_field eq 'nw_dst') { #SET_NW_DST + ($nw_addr_org, $ok_org) = NF2::IP_hdr::getIP($chg_val); + $action_mod_args = { + type => $enums{'OFPAT_SET_NW_DST'}, + len => $ofp->sizeof('ofp_action_nw_addr'), + nw_addr => $nw_addr_org, + }; + $action_mod = $ofp->pack('ofp_action_nw_addr', $action_mod_args); + } elsif ($chg_field eq 'nw_tos') { #SET_NW_TOS + $action_mod_args = { + type => $enums{'OFPAT_SET_NW_TOS'}, + len => $ofp->sizeof('ofp_action_nw_tos'), + nw_tos => $chg_val, + pad => \@pad_3, + }; + $action_mod = $ofp->pack('ofp_action_nw_tos', $action_mod_args); + } elsif ($chg_field eq 'tp_src') { #SET_TP_SRC + $action_mod_args = { + type => $enums{'OFPAT_SET_TP_SRC'}, + len => $ofp->sizeof('ofp_action_tp_port'), + tp_port => $chg_val, + pad => \@pad_2, + }; + $action_mod = $ofp->pack('ofp_action_tp_port', $action_mod_args); + } elsif ($chg_field eq 'tp_dst') { #SET_TP_DST + $action_mod_args = { + type => $enums{'OFPAT_SET_TP_DST'}, + len => $ofp->sizeof('ofp_action_tp_port'), + tp_port => $chg_val, + pad => \@pad_2, + }; + $action_mod = $ofp->pack('ofp_action_tp_port', $action_mod_args); + } elsif ($chg_field eq 'strip_vlan') { #STRIP_VLAN + $action_mod_args = { + type => $enums{'OFPAT_STRIP_VLAN'}, + len => $ofp->sizeof('ofp_action_header'), + pad => \@pad_4, + }; + $action_mod = $ofp->pack('ofp_action_header', $action_mod_args); + } elsif ($chg_field eq 'vlan_vid') { #SET_VLAN_VID + $action_mod_args = { + type => $enums{'OFPAT_SET_VLAN_VID'}, + len => $ofp->sizeof('ofp_action_vlan_vid'), + vlan_vid => $chg_val, + pad => \@pad_2, + }; + $action_mod = $ofp->pack('ofp_action_vlan_vid', $action_mod_args); + } elsif ($chg_field eq 'vlan_pcp') { #SET_VLAN_PCP + $chg_vlan_pcp_val = ($chg_val>>13) & 0x0007; + $action_mod_args = { + type => $enums{'OFPAT_SET_VLAN_PCP'}, + len => $ofp->sizeof('ofp_action_vlan_pcp'), + vlan_pcp => $chg_vlan_pcp_val, + pad => \@pad_3, + }; + $action_mod = $ofp->pack('ofp_action_vlan_pcp', $action_mod_args); } else { - $action_mod = undef; + $action_mod = undef; } + } else { + $action_mod = undef; + } - if (defined $action_mod) { - $flow_mod_pkt = $flow_mod . $action_mod . $action_output; - } else { - $flow_mod_pkt = $flow_mod . $action_output; - } - return $flow_mod_pkt; + if (defined $action_mod) { + $flow_mod_pkt = $flow_mod . $action_mod . $action_output; + } else { + $flow_mod_pkt = $flow_mod . $action_output; + } + + return $flow_mod_pkt; } sub create_flow_mod_from_udp_action { diff --git a/regress/projects/black_box/regress/test_set_nw_dst/run.pl b/regress/projects/black_box/regress/test_set_nw_dst/run.pl new file mode 100755 index 0000000..c52f056 --- /dev/null +++ b/regress/projects/black_box/regress/test_set_nw_dst/run.pl @@ -0,0 +1,95 @@ +#!/usr/bin/perl -w +# test_set_nw_dst + +use strict; +use OF::Includes; + +sub send_expect_exact { + my ($ofp, $sock, $options_ref, $in_port_offset, $out_port_offset, $max_idle, $pkt_len) = @_; + + my $in_port = $in_port_offset + $$options_ref{'port_base'}; + my $out_port = $out_port_offset + $$options_ref{'port_base'}; + + # in_port refers to the flow mod entry's input + + # Create the payload ourselves to make sure the two packets match + # Jean II + my $pkt_payload = [map {int(rand(256))} (1..($pkt_len - 8 - 4 - 16 - 14))]; + + # This is the packet we are sending... - Jean II + my $test_pkt_args = { + DA => "00:00:00:00:00:0" . ($out_port + 1), + SA => "00:00:00:00:00:0" . ($in_port + 1), + src_ip => "192.168.200." . ($in_port + 1), + dst_ip => "192.168.201." . ($out_port + 1), + ttl => 64, + len => $pkt_len, + src_port => 1, + dst_port => 0, + data => $pkt_payload + }; + my $test_pkt = new NF2::UDP_pkt(%$test_pkt_args); + + # This is the packet we are expecting to receive - Jean II + my $expect_pkt_args = { + DA => "00:00:00:00:00:0" . ($out_port + 1), + SA => "00:00:00:00:00:0" . ($in_port + 1), + src_ip => "192.168.200." . ($in_port + 1), + dst_ip => ($out_port + 1) . ".201.168.192", + ttl => 64, + len => $pkt_len, + src_port => 1, + dst_port => 0, + data => $pkt_payload + }; + my $expect_pkt = new NF2::UDP_pkt(%$expect_pkt_args); + + #print HexDump ($test_pkt->packed); + + my $wildcards = 0x0; # exact match + my $flags = $enums{'OFPFF_SEND_FLOW_EXP'}; # want flow expiry + + # Get to the IP address in the expected packet - Jean II + my $ref_to_ip_hdr = ( $expect_pkt->{'IP_hdr'} ); + my $ip_hdr_bytes = $$ref_to_ip_hdr->{'bytes'}; + my @dst_ip_subarray = @{$ip_hdr_bytes}[ 16 .. 19 ]; + my $dst_ip_inverted = ((2**24) * $dst_ip_subarray[0] + + (2**16) * $dst_ip_subarray[1] + + (2**8) * $dst_ip_subarray[2] + + $dst_ip_subarray[3]); + + my $flow_mod_pkt = create_flow_mod_from_udp_action($ofp, $test_pkt, $in_port, $out_port, $max_idle, $flags, $wildcards, 'OFPFC_ADD', 'nw_dst', $dst_ip_inverted); + + #print HexDump($flow_mod_pkt); + + # Send 'flow_mod' message + print $sock $flow_mod_pkt; + print "sent flow_mod message\n"; + + # Give OF switch time to process the flow mod + usleep($$options_ref{'send_delay'}); + + # Send a packet - ensure packet comes out desired port + nftest_send("eth" . ($in_port_offset + 1), $test_pkt->packed); + nftest_expect("eth" . ($out_port_offset + 1), $expect_pkt->packed); +} + +sub test_set_nw_dst { + my ($ofp, $sock, $options_ref, $i, $j, $wildcards) = @_; + + my $max_idle = $$options_ref{'max_idle'}; + my $pkt_len = $$options_ref{'pkt_len'}; + my $pkt_total = $$options_ref{'pkt_total'}; + + send_expect_exact($ofp, $sock, $options_ref, $i, $j, $max_idle, $pkt_len); + wait_for_flow_expired($ofp, $sock, $options_ref, $pkt_len, $pkt_total); +} + +sub my_test { + my ($sock, $options_ref) = @_; + + # send from every port to every other port + for_all_port_pairs($ofp, $sock, $options_ref, \&test_set_nw_dst, 0x0); +} + +run_black_box_test(\&my_test, \@ARGV); diff --git a/regress/projects/black_box/regress/test_set_nw_tos/run.pl b/regress/projects/black_box/regress/test_set_nw_tos/run.pl new file mode 100755 index 0000000..ddbbc98 --- /dev/null +++ b/regress/projects/black_box/regress/test_set_nw_tos/run.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl -w +# test_set_nw_tos + +use strict; +use OF::Includes; + +sub send_expect_exact { + my ($ofp, $sock, $options_ref, $in_port_offset, $out_port_offset, $max_idle, $pkt_len) = @_; + + my $in_port = $in_port_offset + $$options_ref{'port_base'}; + my $out_port = $out_port_offset + $$options_ref{'port_base'}; + + # in_port refers to the flow mod entry's input + + # Create the payload ourselves to make sure the two packets match + # Jean II + my $pkt_payload = [map {int(rand(256))} (1..($pkt_len - 8 - 4 - 16 - 14))]; + + # This is the packet we are sending... - Jean II + my $test_pkt_args = { + DA => "00:00:00:00:00:0" . ($out_port + 1), + SA => "00:00:00:00:00:0" . ($in_port + 1), + src_ip => "192.168.200." . ($in_port + 1), + dst_ip => "192.168.201." . ($out_port + 1), + tos => 0xa9, + ttl => 64, + len => $pkt_len, + src_port => 1, + dst_port => 0, + data => $pkt_payload + }; + my $test_pkt = new NF2::UDP_pkt(%$test_pkt_args); + + # This is the packet we are expecting to receive - Jean II + my $expect_pkt_args = { + DA => "00:00:00:00:00:0" . ($out_port + 1), + SA => "00:00:00:00:00:0" . ($in_port + 1), + src_ip => "192.168.200." . ($in_port + 1), + dst_ip => "192.168.201." . ($out_port + 1), + tos => 0x55, + ttl => 64, + len => $pkt_len, + src_port => 1, + dst_port => 0, + data => $pkt_payload + }; + my $expect_pkt = new NF2::UDP_pkt(%$expect_pkt_args); + + #print HexDump ($test_pkt->packed); + + my $wildcards = 0x0; # exact match + my $flags = $enums{'OFPFF_SEND_FLOW_EXP'}; # want flow expiry + my $nw_tos = 0x56; + + my $flow_mod_pkt = create_flow_mod_from_udp_action($ofp, $test_pkt, $in_port, $out_port, $max_idle, $flags, $wildcards, 'OFPFC_ADD', 'nw_tos', $nw_tos); + + #print HexDump($flow_mod_pkt); + + # Send 'flow_mod' message + print $sock $flow_mod_pkt; + print "sent flow_mod message\n"; + + # Give OF switch time to process the flow mod + usleep($$options_ref{'send_delay'}); + + # Send a packet - ensure packet comes out desired port + nftest_send("eth" . ($in_port_offset + 1), $test_pkt->packed); + nftest_expect("eth" . ($out_port_offset + 1), $expect_pkt->packed); +} + +sub test_set_nw_tos { + my ($ofp, $sock, $options_ref, $i, $j, $wildcards) = @_; + + my $max_idle = $$options_ref{'max_idle'}; + my $pkt_len = $$options_ref{'pkt_len'}; + my $pkt_total = $$options_ref{'pkt_total'}; + + send_expect_exact($ofp, $sock, $options_ref, $i, $j, $max_idle, $pkt_len); + wait_for_flow_expired($ofp, $sock, $options_ref, $pkt_len, $pkt_total); +} + +sub my_test { + my ($sock, $options_ref) = @_; + + # send from every port to every other port + for_all_port_pairs( $ofp, $sock, $options_ref, \&test_set_nw_tos, 0x0); +} + +run_black_box_test(\&my_test, \@ARGV); diff --git a/regress/projects/black_box/regress/tests.txt b/regress/projects/black_box/regress/tests.txt index a5f9ba1..925538c 100644 --- a/regress/projects/black_box/regress/tests.txt +++ b/regress/projects/black_box/regress/tests.txt @@ -34,6 +34,8 @@ test_drop_exact/run.pl test_forward_exact_modify_action/run.pl test_forward_wildcard_modify_action/run.pl test_flow_mod_check/run.pl +test_set_nw_dst/run.pl +test_set_nw_tos/run.pl ## ICMP handling Tests test_forward_exact_icmp_port/run.pl diff --git a/udatapath/dp_act.c b/udatapath/dp_act.c index beef81e..26edcc0 100644 --- a/udatapath/dp_act.c +++ b/udatapath/dp_act.c @@ -197,6 +197,33 @@ set_nw_addr(struct ofpbuf *buffer, struct sw_flow_key *key, } } +static void +set_nw_tos(struct ofpbuf *buffer, struct sw_flow_key *key, + const struct ofp_action_header *ah) +{ + struct ofp_action_nw_tos *nt = (struct ofp_action_nw_tos *)ah; + uint16_t eth_proto = ntohs(key->flow.dl_type); + + if (eth_proto == ETH_TYPE_IP) { + struct ip_header *nh = buffer->l3; + uint8_t new, *field; + + /* JeanII : Set only 6 bits, don't clobber ECN */ + new = (nt->nw_tos & 0xFC) | (nh->ip_tos & 0x03); + + /* Get address of field */ + field = &nh->ip_tos; + + /* jklee : ip tos field is not included in TCP pseudo header. + * Need magic as update_csum() don't work with 8 bits. */ + nh->ip_csum = recalc_csum32(nh->ip_csum, htons((uint16_t)*field), + htons((uint16_t)new)); + + /* Change the IP ToS bits */ + *field = new; + } +} + static void set_tp_port(struct ofpbuf *buffer, struct sw_flow_key *key, const struct ofp_action_header *ah) @@ -283,6 +310,12 @@ static const struct openflow_action of_actions[] = { NULL, set_nw_addr }, + [OFPAT_SET_NW_TOS] = { + sizeof(struct ofp_action_nw_tos), + sizeof(struct ofp_action_nw_tos), + NULL, + set_nw_tos + }, [OFPAT_SET_TP_SRC] = { sizeof(struct ofp_action_tp_port), sizeof(struct ofp_action_tp_port), diff --git a/utilities/dpctl.8.in b/utilities/dpctl.8.in index 0efc66e..d028ff7 100644 --- a/utilities/dpctl.8.in +++ b/utilities/dpctl.8.in @@ -410,6 +410,9 @@ Modifies the destination mac address on a packet, e.g., actions=mod_dl_dst:12:34 .IP \fBmod_dl_src\fR:\fIsrc_mac\fR Modifies the source mac address on a packet, e.g., actions=mod_dl_src:12:34:56:78:9a:bc +.IP \fBmod_nw_tos\fR:\fItos/dscp\fR +Modifies the ToS/DSCP (only 6-bits, not modify reserved 2-bits for future use) field of IPv4 header on a packet. + .IP \fBstrip_vlan\fR Strips the VLAN tag from a packet if it is present. .RE diff --git a/utilities/dpctl.c b/utilities/dpctl.c index bdae872..b6e205d 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -694,9 +694,8 @@ do_dump_tables(const struct settings *s UNUSED, int argc UNUSED, char *argv[]) dump_trivial_stats_transaction(argv[1], OFPST_TABLE); } - static uint32_t -str_to_u32(const char *str) +str_to_u32(const char *str) { char *tail; uint32_t value; @@ -806,7 +805,11 @@ str_to_action(char *str, struct ofpbuf *b) arg++; } - if (!strcasecmp(act, "mod_vlan_vid")) { + if (!strcasecmp(act, "mod_nw_tos")) { + struct ofp_action_nw_tos *va; + va = put_action(b, sizeof *va, OFPAT_SET_NW_TOS); + va->nw_tos = str_to_u32(arg); + } else if (!strcasecmp(act, "mod_vlan_vid")) { struct ofp_action_vlan_vid *va; va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID); va->vlan_vid = htons(str_to_u32(arg)); diff --git a/utilities/wireshark_dissectors/openflow/packet-openflow.c b/utilities/wireshark_dissectors/openflow/packet-openflow.c index f08d7bb..f469464 100644 --- a/utilities/wireshark_dissectors/openflow/packet-openflow.c +++ b/utilities/wireshark_dissectors/openflow/packet-openflow.c @@ -99,12 +99,13 @@ static const value_string names_ofp_action_type[] = { { OFPAT_SET_DL_DST, "Ethernet destination address" }, { OFPAT_SET_NW_SRC, "IP source address" }, { OFPAT_SET_NW_DST, "IP destination address" }, + { OFPAT_SET_NW_TOS, "Set IP TOS field" }, { OFPAT_SET_TP_SRC, "TCP/UDP source port" }, { OFPAT_SET_TP_DST, "TCP/UDP destination port"}, { OFPAT_VENDOR, "Vendor-defined action"}, { 0, NULL } }; -#define NUM_ACTIONS_FLAGS 10 +#define NUM_ACTIONS_FLAGS 11 #define NUM_PORT_CONFIG_FLAGS 7 #define NUM_PORT_STATE_FLAGS 1 #define NUM_PORT_FEATURES_FLAGS 12 @@ -414,6 +415,7 @@ static gint ofp_action_vlan_vid = -1; static gint ofp_action_vlan_pcp = -1; static gint ofp_action_dl_addr = -1; static gint ofp_action_nw_addr = -1; +static gint ofp_action_nw_tos = -1; static gint ofp_action_tp_port = -1; static gint ofp_action_vendor = -1; static gint ofp_action_unknown = -1; @@ -1076,6 +1078,9 @@ void proto_register_openflow() { &ofp_action_nw_addr, { "IP Addr", "of.action_nw_addr", FT_IPv4, BASE_NONE, NO_STRINGS, NO_MASK, "IP Addr", HFILL }}, + { &ofp_action_nw_tos, + { "IP TOS bits", "of.action_vlan_pcp", FT_UINT8, BASE_DEC, NO_STRINGS, NO_MASK, "IP TOS bits", HFILL }}, + { &ofp_action_tp_port, { "Port", "of.action_tp_port", FT_UINT16, BASE_DEC, NO_STRINGS, NO_MASK, "TCP/UDP Port", HFILL }}, @@ -1169,9 +1174,12 @@ void proto_register_openflow() { " IP destination address", "of.sf_actions_ip_dst_addr", FT_UINT32, BASE_DEC, VALS(names_choice), 1 << OFPAT_SET_NW_DST, "IP destination address", HFILL }}, { &ofp_switch_features_actions[8], - { " TCP/UDP source", "of.sf_actions_src_port", FT_UINT32, BASE_DEC, VALS(names_choice), 1 << OFPAT_SET_TP_SRC, "TCP/UDP source port", HFILL }}, + { " Set IP TOS bits", "of.sf_actions_ip_tos", FT_UINT32, BASE_DEC, VALS(names_choice), 1 << OFPAT_SET_NW_TOS, "Set IP TOS bits", HFILL }}, { &ofp_switch_features_actions[9], + { " TCP/UDP source", "of.sf_actions_src_port", FT_UINT32, BASE_DEC, VALS(names_choice), 1 << OFPAT_SET_TP_SRC, "TCP/UDP source port", HFILL }}, + + { &ofp_switch_features_actions[10], { " TCP/UDP destination", "of.sf_actions_dst_port", FT_UINT32, BASE_DEC, VALS(names_choice), 1 << OFPAT_SET_TP_DST, "TCP/UDP destination port", HFILL }}, { &ofp_switch_features_ports_hdr, @@ -2221,6 +2229,11 @@ static gint dissect_action(proto_tree* tree, proto_item* item, tvbuff_t *tvb, pa add_child( action_tree, ofp_action_nw_addr, tvb, offset, 4 ); break; + case OFPAT_SET_NW_TOS: + add_child( action_tree, ofp_action_nw_tos, tvb, offset, 1); + dissect_pad(action_tree, offset, 3); + break; + case OFPAT_SET_TP_SRC: case OFPAT_SET_TP_DST: add_child( action_tree, ofp_action_tp_port, tvb, offset, 2 );