#!/bin/sh # Install ebtables rules BR_RULE="" BR6_RULE="" BR_RULE_DSCP2PBIT="" DSCP2PBIT_MAPPING="" init_broute_rule() { BR_RULE="" BR6_RULE="" } init_broute_dscp2pbit_rule() { BR_RULE_DSCP2PBIT="" DSCP2PBIT_MAPPING="" } broute_filter_on_l3_if() { BR_RULE="$BR_RULE --logical-in $1" } broute_filter_on_src_if() { BR_RULE="$BR_RULE --in-if $1" } broute_filter_on_src_mac() { BR_RULE="$BR_RULE --src $1" } broute_filter_on_dst_mac() { BR_RULE="$BR_RULE --dst $1" } broute_filter_on_pcp() { # 5.04 onwards the vlan extension in ebtables is used for classification # on the basis of vlan params which needs proto to be defined as 802_1Q in # order to add a rule, now, proto can also be defined by specifying proto uci # option as well as the rule may have a vlan id as well in which case the # proto will already be present, hence, this check to not add --proto more # than once case "$BR_RULE" in *proto*) BR_RULE="$BR_RULE --vlan-prio $1" ;; *) BR_RULE="$BR_RULE --proto 802_1Q --vlan-prio $1" ;; esac } broute_filter_on_ether_type() { BR_RULE="$BR_RULE --proto $1" } broute_filter_on_ether_type6() { BR6_RULE="$BR6_RULE --proto IPv6" } ebt_match_src_ip() { BR_RULE="$BR_RULE --ip-src $1" } ebt_match_dst_ip() { BR_RULE="$BR_RULE --ip-dst $1" } ebt_match_ipv6_src_ip() { BR_RULE="$BR_RULE --ip6-src $1" } ebt_match_ipv6_dst_ip() { BR_RULE="$BR_RULE --ip6-dst $1" } ebt_match_ip_src_port() { BR_RULE="$BR_RULE --ip-source-port $1" } ebt_match_ip_dst_port() { BR_RULE="$BR_RULE --ip-destination-port $1" } ebt_match_ipv6_src_port() { #when ethertype is not configured by user then both proto rules of ipv4 #and ipv6 to be installed so update BR6_RULE string as well otherwise #update BR_RULE only for installation of ipv6 proto rule only. if [ -n "$BR6_RULE" ]; then BR6_RULE="$BR6_RULE --ip6-source-port $1" else BR_RULE="$BR_RULE --ip6-source-port $1" fi } ebt_match_ipv6_dst_port() { #when ethertype is not configured by user then both proto rules of ipv4 #and ipv6 to be installed so update BR6_RULE string as well otherwise #update BR_RULE only for installation of ipv6 proto rule only. if [ -n "$BR6_RULE" ]; then BR6_RULE="$BR6_RULE --ip6-destination-port $1" else BR_RULE="$BR_RULE --ip6-destination-port $1" fi } ebt_match_ip_protocol() { BR_RULE="$BR_RULE --ip-proto $1" } ebt_match_ip_icmp_type() { BR_RULE="$BR_RULE --ip-icmp-type $1" } ebt_match_ipv6_protocol() { #when ethertype is not configured by user then both proto rules of ipv4 #and ipv6 to be installed so update BR6_RULE string as well otherwise #update BR_RULE only for installation of ipv6 proto rule only. if [ -n "$BR6_RULE" ]; then BR6_RULE="$BR6_RULE --ip6-proto $1" else BR_RULE="$BR_RULE --ip6-proto $1" fi } ebt_match_ipv6_icmp_type() { #when ethertype is not configured by user then both proto rules of ipv4 #and ipv6 to be installed so update BR6_RULE string as well otherwise #update BR_RULE only for installation of ipv6 proto rule only. if [ -n "$BR6_RULE" ]; then BR6_RULE="$BR6_RULE --ip6-icmp-type $1" else BR_RULE="$BR_RULE --ip6-icmp-type $1" fi } broute_filter_on_vid() { if [ -z "$1" ] || [ "$1" -lt 0 ]; then return fi case "$BR_RULE" in *proto*) BR_RULE="$BR_RULE --vlan-id $1" ;; *) BR_RULE="$BR_RULE --proto 802_1Q --vlan-id $1" ;; esac } broute_append_rule() { # if src_if is loopback, then add the rule to OUTPUT(qos_output) chain of nat table if [ "$src_if" = "lo" ]; then echo "ebtables --concurrent -t nat -A qos_output $BR_RULE" >> /tmp/qos/classify.ebtables if [ -n "$BR6_RULE" ]; then echo "ebtables --concurrent -t nat -A qos_output $BR6_RULE" >> /tmp/qos/classify.ebtables fi return fi local broute_chain="$1" #when ethertype is not configured by user then both proto rules of ipv4 #and ipv6 to be installed otherwise install ipv6 proto rule only. echo "ebtables --concurrent -t broute -A $broute_chain $BR_RULE" >> /tmp/qos/classify.ebtables if [ -n "$BR6_RULE" ]; then echo "ebtables --concurrent -t broute -A $broute_chain $BR6_RULE" >> /tmp/qos/classify.ebtables fi } broute_apply_dscp2pbit_rule() { # Write dscp2pbit broute rule to classify.ebtables file echo "ebtables --concurrent -t broute -A dscp2pbits -p 0x8100 $BR_RULE_DSCP2PBIT" >> /tmp/qos/classify.ebtables } broute_rule_set_xlate_vid_pbit() { local vid_mark="$1" local pcp_mark="$2" BR_RULE="$BR_RULE -j vlantranslation" [ -n "$vid_mark" ] && BR_RULE="$BR_RULE --vlanxlate-vid-set $vid_mark" [ -n "$pcp_mark" ] && BR_RULE="$BR_RULE --vlanxlate-prio-set $pcp_mark" BR_RULE="$BR_RULE --vlanxlate-target CONTINUE" } set_ip_addr() { local cid="$1" local match_src_ip_func="$2" local match_dst_ip_func="$3" config_get src_ip "$cid" "src_ip" config_get dst_ip "$cid" "dest_ip" if [ -n "$src_ip" ]; then $match_src_ip_func "$src_ip" fi if [ -n "$dst_ip" ]; then $match_dst_ip_func "$dst_ip" fi } set_ports() { local cid="$1" local match_src_port_func="$2" local match_dst_port_func="$3" local src_port="" local dst_port="" local src_port_range="" local dst_port_range="" config_get src_port "$cid" "src_port" config_get dst_port "$cid" "dest_port" config_get src_port_range "$cid" "src_port_range" config_get dst_port_range "$cid" "dest_port_range" if [ -n "$src_port" ] && [ -n "$src_port_range" ] ; then $match_src_port_func "$src_port:$src_port_range" elif [ -n "$src_port" ] ; then $match_src_port_func "$src_port" fi if [ -n "$dst_port" ] && [ -n "$dst_port_range" ] ; then $match_dst_port_func "$dst_port:$dst_port_range" elif [ -n "$dst_port" ] ; then $match_dst_port_func "$dst_port" fi } protocol_string_to_num() { local value="-1" case "$1" in TCP|tcp) value=6 ;; UDP|udp) value=17 ;; ICMP|icmp) value=1 ;; ICMPv6|icmpv6) value=58 ;; IGMP|igmp) value=2 ;; SCTP|sctp) value=132 ;; *[0-9]*) value="$1" ;; *) value=-1 ;; esac echo $value } handle_ebtables_rules() { local sid="$1" local is_l2_rule=0 local src_dhcp_options="" local dst_dhcp_options="" local protocol="" local ip_version="" config_get pcp_mark "$sid" "pcp_mark" config_get dscp_filter "$sid" "dscp_filter" # return if its a classfy section for DSCP to p-bit mapping if [ -n "$pcp_mark" ] && [ -n "$dscp_filter" ]; then return fi init_broute_rule config_get src_if "$sid" "ifname" config_get src_mac "$sid" "src_mac" config_get dst_mac "$sid" "dst_mac" config_get pcp_check "$sid" "pcp_check" config_get eth_type "$sid" "ethertype" config_get vid "$sid" "vid_check" config_get dhcp_type "$sid" "dhcp_type" # dhcpv4 or v6 config_get src_vcid "$sid" "src_vendor_class_id" # dhcp option 60 config_get dst_vcid "$sid" "dst_vendor_class_id" # dhcp option 60 config_get src_clid "$sid" "src_client_id" # dhcp option 61 config_get dst_clid "$sid" "dst_client_id" # dhcp option 61 config_get src_ucid "$sid" "src_user_class_id" # dhcp option 77 config_get dst_ucid "$sid" "dst_user_class_id" # dhcp option 77 config_get traffic_class "$sid" "traffic_class" config_get protocol "$sid" "proto" config_get all_interfaces "$sid" "all_interfaces" config_get l3_ifname "$sid" "l3_ifname" config_get vid_mark "$sid" "vid_mark" if [ -n "$l3_ifname" ]; then broute_filter_on_l3_if "$l3_ifname" is_l2_rule=1 fi if [ "$all_interfaces" == "1" ]; then is_l2_rule=1 elif [ -n "$src_if" ]; then for interf in $(jsonfilter -i /etc/board.json -e @.network.lan.ports[*] -e @.network.lan.device -e @.network.wan.device | xargs); do if [ "$src_if" == "$interf" ]; then src_if="$src_if+" broute_filter_on_src_if "$src_if" is_l2_rule=1 fi done fi if [ -n "$src_mac" ]; then broute_filter_on_src_mac "$src_mac" is_l2_rule=1 fi if [ -n "$dst_mac" ]; then broute_filter_on_dst_mac "$dst_mac" is_l2_rule=1 fi if [ -n "$pcp_check" ]; then broute_filter_on_pcp "$pcp_check" is_l2_rule=1 fi if [ -n "$eth_type" ]; then broute_filter_on_ether_type "$eth_type" is_l2_rule=1 fi if [ -n "$vid" ]; then broute_filter_on_vid "$vid" is_l2_rule=1 fi case $(echo "$eth_type" | tr 'a-z' 'A-Z') in IPV4|0800|800|0X0800) ip_version=4 ;; IPV6|86DD|0X86DD) ip_version=6 ;; *) if [ -z "$eth_type" ]; then case "$src_ip$dst_ip" in *.*) ip_version=4 broute_filter_on_ether_type "IPv4" ;; *:*) ip_version=6 broute_filter_on_ether_type "IPv6" ;; *) if [ -n "$protocol" ] || [ -n "$dscp_filter" ]; then # Neither ether_type nor ip address used, # ethertype is not configured by user, so install # both proto IPv4 and IPv6 rule (version 1) ip_version=1 BR6_RULE="$BR_RULE" broute_filter_on_ether_type "IPv4" broute_filter_on_ether_type6 "IPv6" fi esac fi esac if [ "$ip_version" == "4" ] || [ "$ip_version" == "1" ]; then broute_ipv4_rule_options "$sid" is_l2_rule=1 fi if [ "$ip_version" == "6" ] || [ "$ip_version" == "1" ]; then broute_ipv6_rule_options "$sid" is_l2_rule=1 fi # first process options that will help figure our source mac address # dhcp option for "vendor class id" if [ -n "$src_vcid" ]; then src_dhcp_options="$src_dhcp_options vcid=$src_vcid" is_l2_rule=1 fi # dhcp option for "client id" if [ -n "$src_clid" ]; then src_dhcp_options="$src_dhcp_options clid=$src_clid" is_l2_rule=1 fi # dhcp option for "user class id" if [ -n "$src_ucid" ]; then src_dhcp_options="$src_dhcp_options ucid=$src_ucid" is_l2_rule=1 fi # if src mac is already a classification criteria, then it # does not really make sense to add it as a criteria to # filter packets again based on source mac if [ -n "$src_dhcp_options" ] && [ -z "$src_mac" ]; then comp="$(grep -i "$src_dhcp_options" /tmp/dhcp.client.options)" if [ -n "$comp" ]; then s_mac_add="$(echo $comp | head -n1 | awk '{print $1;}')" if [ -n "$s_mac_add" ]; then broute_filter_on_src_mac "$s_mac_add" fi fi fi # Now process options that will help figure our destination mac address # dhcp option for "vendor class id" if [ -n "$dst_vcid" ]; then dst_dhcp_options="$dst_dhcp_options vcid=$dst_vcid" is_l2_rule=1 fi # dhcp option for "client id" if [ -n "$dst_clid" ]; then dst_dhcp_options="$dst_dhcp_options clid=$dst_clid" is_l2_rule=1 fi # dhcp option for "user class id" if [ -n "$dst_ucid" ]; then dst_dhcp_options="$dst_dhcp_options ucid=$dst_ucid" is_l2_rule=1 fi # if dst mac is already a classification criteria, then it # does not really make sense to add it as a criteria to # filter packets again based on destination mac if [ -n "$dst_dhcp_options" ] && [ -z "$dst_mac" ] ; then comp="$(grep -i "$dst_dhcp_options" /tmp/dhcp.client.options)" if [ -n "$comp" ]; then d_mac_add="$(echo $comp | head -n1 | awk '{print $1;}')" if [ -n "$d_mac_add" ]; then broute_filter_on_dst_mac "$d_mac_add" fi fi fi if [ $is_l2_rule -eq 0 ]; then return fi [ -n "$traffic_class" ] && broute_rule_set_traffic_class "$traffic_class" if [ -n "$vid_mark" ] || [ -n "$pcp_mark" ]; then broute_rule_set_xlate_vid_pbit "$vid_mark" "$pcp_mark" fi if [ -n "$BR_RULE" ]; then if [ -n "$vid_mark" ] || [ -n "$pcp_mark" ]; then broute_append_rule "prevlanxlate" "$src_if" else broute_append_rule "qos" "$src_if" fi fi } handle_ebtables_dscp2pbit() { local in_if=$1 local dscp_filter="" local pcp_mark="" local ifname="" local dscp2pbit_mapping_list="" local corder_file="/tmp/qos/classify.order" if [ -z "$in_if" ]; then return fi init_broute_dscp2pbit_rule while read -r line; do line_cid=${line#*_} config_get dscp_filter "$line_cid" "dscp_filter" config_get pcp_mark "$line_cid" "pcp_mark" # return if not a dscp to p-bit rule if [ -z "$dscp_filter" ] || [ -z "$pcp_mark" ]; then continue fi config_get ifname "$line_cid" "ifname" # return if this config is not for the currently processing interface (in_if) if [ -n "$ifname" ] && [ "$ifname" != "$in_if" ]; then continue fi dscp2pbit_mapping_list="$dscp2pbit_mapping_list,$dscp_filter=$pcp_mark" done < "$corder_file" # if not dscp2pbit config found for our interface, return [ -z "$dscp2pbit_mapping_list" ] && return # remove first character(comma) from the dscp2pbit_mapping_list, not required. dscp2pbit_mapping_list="${dscp2pbit_mapping_list:1}" # construct ebtables rule: BR_RULE_DSCP2PBIT=" -i $in_if -j dscp2pbit --dscp2pbit-mapping $dscp2pbit_mapping_list --dscp2pbit-target CONTINUE" } flush_ebtables_chains() { echo "ebtables --concurrent -t nat -F qos_output" > /tmp/qos/classify.ebtables echo "ebtables --concurrent -t broute -F qos" > /tmp/qos/classify.ebtables echo "ebtables --concurrent -t broute -F dscp2pbits" >> /tmp/qos/classify.ebtables echo "ebtables --concurrent -t broute -F prevlanxlate" >> /tmp/qos/classify.ebtables }