def create_dot1x_flow_pair(self, dot1x_port, nfv_sw_port, mac): ofmsgs = [ self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=dot1x_port.number, eth_type=valve_packet.ETH_EAPOL), priority=self.acl_priority, inst=[ valve_of.apply_actions([ self.port_acl_table.set_field(eth_dst=mac), valve_of.output_port(nfv_sw_port.number) ]) ], ), self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=nfv_sw_port.number, eth_type=valve_packet.ETH_EAPOL, eth_src=mac), priority=self.acl_priority, inst=[ valve_of.apply_actions([ self.port_acl_table.set_field( eth_src=valve_packet.EAPOL_ETH_DST), valve_of.output_port(dot1x_port.number) ]) ], ) ] return ofmsgs
def create_dot1x_flow_pair(self, dot1x_port, nfv_sw_port, mac): ofmsgs = [ self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=dot1x_port.number, eth_type=valve_packet.ETH_EAPOL), priority=self.acl_priority, inst=[valve_of.apply_actions([ self.port_acl_table.set_field(eth_dst=mac), valve_of.output_port(nfv_sw_port.number)])], ), self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=nfv_sw_port.number, eth_type=valve_packet.ETH_EAPOL, eth_src=mac), priority=self.acl_priority, inst=[valve_of.apply_actions([ self.port_acl_table.set_field( eth_src=valve_packet.EAPOL_ETH_DST), valve_of.output_port(dot1x_port.number) ])], ) ] return ofmsgs
def create_dot1x_flow_pair(self, port_num, nfv_sw_port_num, mac): """Create dot1x flow pair""" ofmsgs = [ self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=port_num, eth_type=valve_packet.ETH_EAPOL), priority=self.dot1x_static_rules_priority, inst=[valve_of.apply_actions([ self.port_acl_table.set_field(eth_dst=mac), valve_of.output_port(nfv_sw_port_num)])], ), self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=nfv_sw_port_num, eth_type=valve_packet.ETH_EAPOL, eth_src=mac), priority=self.dot1x_static_rules_priority, inst=[valve_of.apply_actions([ self.port_acl_table.set_field( eth_src=valve_packet.EAPOL_ETH_DST), valve_of.output_port(port_num) ])], ) ] return ofmsgs
def _add_resolved_route(self, vlan, ip_gw, ip_dst, eth_dst, is_updated): ofmsgs = [] if is_updated: self.logger.info( 'Updating next hop for route %s via %s (%s) on VLAN %u' % (ip_dst, ip_gw, eth_dst, vlan.vid)) ofmsgs.extend(self._del_route_flows(vlan, ip_dst)) else: self.logger.info('Adding new route %s via %s (%s) on VLAN %u' % (ip_dst, ip_gw, eth_dst, vlan.vid)) if self.use_group_table: inst = [ valve_of.apply_actions([ valve_of.group_act( group_id=self._group_id_from_ip_gw(vlan, ip_gw)) ]) ] else: inst = [ valve_of.apply_actions(self._nexthop_actions(eth_dst, vlan)), valve_of.goto_table(self.eth_dst_table) ] for routed_vlan in self._routed_vlans(vlan): in_match = self._route_match(routed_vlan, ip_dst) ofmsgs.append( self.fib_table.flowmod(in_match, priority=self._route_priority(ip_dst), inst=inst)) return ofmsgs
def get_port_acls(self, valve, dot1x_port): """Setup the dot1x forward port acls. Args: dot1x_port: valve: Returns: list of flowmods """ port_acl_table = valve.dp.tables['port_acl'] nfv_sw_port = valve.dp.dot1x['nfv_sw_port'] valve_index = self.dp_id_to_valve_index[valve.dp.dp_id] mac = get_mac_str(valve_index, dot1x_port.number) ofmsgs = [] ofmsgs.append(port_acl_table.flowmod( port_acl_table.match( in_port=dot1x_port.number, eth_type=valve_packet.ETH_EAPOL), priority=valve.dp.highest_priority, inst=[valve_of.apply_actions([ valve_of.set_field(eth_dst=mac), valve_of.output_port(nfv_sw_port)])])) ofmsgs.append(port_acl_table.flowmod( port_acl_table.match( in_port=nfv_sw_port, eth_type=valve_packet.ETH_EAPOL, eth_src=mac), priority=valve.dp.highest_priority, inst=[valve_of.apply_actions([ valve_of.set_field(eth_src=EAPOL_DST), valve_of.output_port(dot1x_port.number)])])) return ofmsgs
def create_flow_pair(self, dot1x_port, nfv_sw_port, valve): """Creates the pair of flows that redirects the eapol packets to/from the supplicant and nfv port Args: dot1x_port (Port): nfv_sw_port (int): valve (Valve): Returns: list """ port_acl_table = valve.dp.tables['port_acl'] valve_index = self.dp_id_to_valve_index[valve.dp.dp_id] mac = get_mac_str(valve_index, dot1x_port.number) if dot1x_port.running(): return [ port_acl_table.flowmod( inst=[valve_of.apply_actions([ valve_of.set_field(eth_dst=mac), valve_of.output_port(nfv_sw_port)])], **FaucetDot1x.get_dot1x_port_match_priority(dot1x_port, port_acl_table, valve)), port_acl_table.flowmod( inst=[valve_of.apply_actions([ valve_of.set_field(eth_src=EAPOL_DST), valve_of.output_port(dot1x_port.number)])], **FaucetDot1x.get_nfv_sw_port_match_priority(mac, nfv_sw_port, port_acl_table, valve) )] return []
def create_dot1x_flow_pair(self, port_num, nfv_sw_port_num, mac): """Create dot1x flow pair""" ofmsgs = [ self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=port_num, eth_type=valve_packet.ETH_EAPOL), priority=self.override_priority, inst=(valve_of.apply_actions([ self.port_acl_table.set_field(eth_dst=mac), valve_of.output_port(nfv_sw_port_num)]),), ), self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=nfv_sw_port_num, eth_type=valve_packet.ETH_EAPOL, eth_src=mac), priority=self.override_priority, inst=(valve_of.apply_actions([ self.port_acl_table.set_field( eth_src=valve_packet.EAPOL_ETH_DST), valve_of.output_port(port_num) ]),), ) ] return ofmsgs
def learn_host_on_vlan_port_flows(self, port, vlan, eth_src, delete_existing, src_rule_idle_timeout, src_rule_hard_timeout, dst_rule_idle_timeout): """Return flows that implement learning a host on a port.""" ofmsgs = [] if port.permanent_learn: # Antispoofing rule for this MAC. ofmsgs.append( self.eth_src_table.flowdrop(self.eth_src_table.match( vlan=vlan, eth_src=eth_src), priority=(self.host_priority - 2))) else: # Delete any existing entries for MAC. # TODO: for LAGs, don't delete entries in the same LAG. if delete_existing: ofmsgs.extend(self.delete_host_from_vlan(eth_src, vlan)) # Associate this MAC with source port. ofmsgs.append( self.eth_src_table.flowmod( self.eth_src_table.match(in_port=port.number, vlan=vlan, eth_src=eth_src), priority=(self.host_priority - 1), inst=[valve_of.goto_table(self.eth_dst_table)], hard_timeout=src_rule_hard_timeout, idle_timeout=src_rule_idle_timeout)) # Output packets for this MAC to specified port. ofmsgs.append( self.eth_dst_table.flowmod( self.eth_dst_table.match(vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=[valve_of.apply_actions(vlan.output_port(port))], idle_timeout=dst_rule_idle_timeout)) # If port is in hairpin mode, install a special rule # that outputs packets destined to this MAC back out the same # port they came in (e.g. multiple hosts on same WiFi AP, # and FAUCET is switching between them on the same port). if port.hairpin: ofmsgs.append( self.eth_dst_table.flowmod( self.eth_dst_table.match(in_port=port.number, vlan=vlan, eth_dst=eth_src), priority=(self.host_priority + 1), inst=[ valve_of.apply_actions( vlan.output_port(port, hairpin=True)) ], idle_timeout=dst_rule_idle_timeout)) return ofmsgs
def build_acl_entry(rule_conf, acl_allow_inst, meters, port_num=None, vlan_vid=None): acl_inst = [] acl_match_dict = {} acl_ofmsgs = [] acl_cookie = None for attrib, attrib_value in list(rule_conf.items()): if attrib == 'in_port': continue if attrib == 'cookie': acl_cookie = attrib_value continue if attrib == 'actions': allow = False allow_specified = False if 'allow' in attrib_value: allow_specified = True if attrib_value['allow'] == 1: allow = True if 'meter' in attrib_value: meter_name = attrib_value['meter'] acl_inst.append( valve_of.apply_meter(meters[meter_name].meter_id)) if 'mirror' in attrib_value: port_no = attrib_value['mirror'] acl_inst.append( valve_of.apply_actions([valve_of.output_port(port_no)])) if not allow_specified: allow = True if 'output' in attrib_value: output_port, output_actions, output_ofmsgs = build_output_actions( attrib_value['output']) acl_inst.append(valve_of.apply_actions(output_actions)) acl_ofmsgs.extend(output_ofmsgs) # if port specified, output packet now and exit pipeline. if output_port is not None: continue if allow: acl_inst.append(acl_allow_inst) else: acl_match_dict[attrib] = attrib_value if port_num is not None: acl_match_dict['in_port'] = port_num if vlan_vid is not None: acl_match_dict['vlan_vid'] = valve_of.vid_present(vlan_vid) try: acl_match = valve_of.match_from_dict(acl_match_dict) except TypeError: assert False, 'invalid type in ACL' return (acl_match, acl_inst, acl_cookie, acl_ofmsgs)
def build_acl_entry(rule_conf, acl_allow_inst, port_num=None, vlan_vid=None): acl_inst = [] match_dict = {} for attrib, attrib_value in list(rule_conf.items()): if attrib == 'in_port': continue if attrib == 'actions': allow = False allow_specified = False if 'allow' in attrib_value: allow_specified = True if attrib_value['allow'] == 1: allow = True if 'mirror' in attrib_value: port_no = attrib_value['mirror'] acl_inst.append( valve_of.apply_actions([valve_of.output_port(port_no)])) if not allow_specified: allow = True if 'output' in attrib_value: output_dict = attrib_value['output'] output_actions = [] output_port = None if 'port' in output_dict: output_port = output_dict['port'] # if destination rewriting selected, rewrite it. if 'dl_dst' in output_dict: output_actions.append( valve_of.set_eth_dst(output_dict['dl_dst'])) # rewrite any VLAN headers. vlan_actions = rewrite_vlan(output_dict) if vlan_actions: output_actions.extend(vlan_actions) # output to a port if specified. if output_port is not None: output_actions.append(valve_of.output_port(output_port)) acl_inst.append(valve_of.apply_actions(output_actions)) # if port specified, output packet now and exit pipeline. if output_port is not None: continue if allow: acl_inst.append(acl_allow_inst) else: match_dict[attrib] = attrib_value if port_num is not None: match_dict['in_port'] = port_num if vlan_vid is not None: match_dict['vlan_vid'] = valve_of.vid_present(vlan_vid) acl_match = valve_of.match_from_dict(match_dict) return acl_match, acl_inst
def add_faucet_vip(self, vlan, faucet_vip): ofmsgs = [] max_prefixlen = faucet_vip.ip.max_prefixlen faucet_vip_host = self._host_from_faucet_vip(faucet_vip) priority = self.route_priority + max_prefixlen faucet_vip_host_nd_mcast = valve_packet.ipv6_link_eth_mcast( valve_packet.ipv6_solicited_node_from_ucast(faucet_vip.ip)) ofmsgs.append(self.valve_flowmod( self.eth_src_table, self.valve_in_match( self.eth_src_table, eth_type=self._eth_type(), vlan=vlan, nw_proto=inet.IPPROTO_ICMPV6, eth_dst=faucet_vip_host_nd_mcast, icmpv6_type=icmpv6.ND_NEIGHBOR_SOLICIT), priority=priority, inst=[ valve_of.apply_actions([valve_of.output_controller()]), valve_of.goto_table(self.flood_table)])) ofmsgs.append(self.valve_flowmod( self.eth_src_table, self.valve_in_match( self.eth_src_table, eth_type=self._eth_type(), eth_dst=self.faucet_mac, vlan=vlan, nw_proto=inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT), priority=priority, inst=[valve_of.apply_actions([valve_of.output_controller()])])) # Initialize IPv6 FIB ofmsgs.append(self.valve_flowmod( self.eth_src_table, self.valve_in_match( self.eth_src_table, eth_type=self._eth_type(), eth_dst=self.faucet_mac, vlan=vlan), priority=self.route_priority, inst=[valve_of.goto_table(self.fib_table)])) ofmsgs.append(self.valve_flowcontroller( self.fib_table, self.valve_in_match( self.fib_table, eth_type=self._eth_type(), vlan=vlan, nw_proto=inet.IPPROTO_ICMPV6, nw_dst=faucet_vip_host, icmpv6_type=icmpv6.ICMPV6_ECHO_REQUEST), priority=priority)) return ofmsgs
def build_tunnel_ofmsgs(rule_conf, acl_table, priority, port_num=None, vlan_vid=None, flowdel=False): """Build a specific tunnel only ofmsgs""" ofmsgs = [] acl_inst = [] acl_match = [] acl_match_dict = {} _, output_actions, output_ofmsgs = build_output_actions( acl_table, rule_conf) ofmsgs.extend(output_ofmsgs) acl_inst.append(valve_of.apply_actions(output_actions)) if port_num is not None: acl_match_dict['in_port'] = port_num if vlan_vid is not None: acl_match_dict['vlan_vid'] = valve_of.vid_present(vlan_vid) acl_match = valve_of.match_from_dict(acl_match_dict) flowmod = acl_table.flowmod(acl_match, priority=priority, inst=acl_inst) if flowdel: ofmsgs.append( acl_table.flowdel(match=acl_match, priority=priority, strict=False)) ofmsgs.append(flowmod) return ofmsgs
def _port_add_vlan_tagged(self, port, vlan, forwarding_table, mirror_act): vlan_inst = [ valve_of.goto_table(forwarding_table) ] if mirror_act: vlan_inst = [valve_of.apply_actions(mirror_act)] + vlan_inst return self._port_add_vlan_rules(port, vlan, vlan_inst)
def add_port(self, port): """Add flows to allow coprocessor to inject or output packets.""" ofmsgs = [] if port.coprocessor: ofmsgs.append( self.vlan_table.flowmod( self.vlan_table.match(in_port=port.number), priority=self.low_priority, inst=(self.vlan_table.goto(self.copro_table), ))) ofmsgs.append( self.eth_src_table.flowmod( match=self.eth_src_table.match(in_port=port.number), priority=self.high_priority, inst=(self.eth_src_table.goto(self.output_table), ))) # TODO: add additional output port strategies (eg. MPLS) and tagged ports vlan_vid_base = port.coprocessor.get('vlan_vid_base', 0) for port_number in self.ports: inst = (valve_of.apply_actions( (valve_of.pop_vlan(), valve_of.output_port(port_number))), ) vid = vlan_vid_base + port_number vlan = OFVLAN(str(vid), vid) match = self.copro_table.match(vlan=vlan) ofmsgs.append( self.copro_table.flowmod(match=match, priority=self.high_priority, inst=inst)) return ofmsgs
def _add_faucet_vip_nd(self, vlan, priority, faucet_vip, faucet_vip_host): faucet_vip_host_nd_mcast = valve_packet.ipv6_link_eth_mcast( valve_packet.ipv6_solicited_node_from_ucast(faucet_vip.ip)) controller_and_flood = [ valve_of.apply_actions([valve_of.output_controller()]), valve_of.goto_table(self.flood_table)] ofmsgs = [] ofmsgs.append(self.eth_src_table.flowmod( self.eth_src_table.match( eth_type=self.ETH_TYPE, eth_dst=faucet_vip_host_nd_mcast, vlan=vlan, nw_proto=inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_NEIGHBOR_SOLICIT), priority=priority, inst=controller_and_flood)) ofmsgs.append(self.eth_src_table.flowcontroller( self.eth_src_table.match( eth_type=self.ETH_TYPE, eth_dst=vlan.faucet_mac, vlan=vlan, nw_proto=inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT), priority=priority)) if faucet_vip.ip in valve_packet.IPV6_LINK_LOCAL: ofmsgs.append(self.eth_src_table.flowmod( self.eth_src_table.match( eth_type=self.ETH_TYPE, eth_dst=valve_packet.IPV6_ALL_ROUTERS_MCAST, vlan=vlan, nw_proto=inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_ROUTER_SOLICIT), priority=priority, inst=controller_and_flood)) return ofmsgs
def output(self, port, vlan, hairpin=False, external_forwarding_requested=None): """Get instructions list to output a packet through the regular pipeline. args: port: Port object of port to output packet to vlan: Vlan object of vlan to output packet on hairpin: if True, hairpinning is required apply_egress_acl: if True the packet will be sent to the egress acl table before being output returns: list of Instructions """ instructions = [] if self.egress_table: metadata, metadata_mask = faucet_md.get_egress_metadata( port.number, vlan.vid) if self.egress_acl_table: instructions.extend(valve_of.metadata_goto_table( metadata, metadata_mask, self.egress_acl_table)) else: instructions.extend(valve_of.metadata_goto_table( metadata, metadata_mask, self.egress_table)) else: instructions.append(valve_of.apply_actions(vlan.output_port( port, hairpin=hairpin, output_table=self.output_table, external_forwarding_requested=external_forwarding_requested))) return instructions
def add_faucet_vip(self, vlan, faucet_vip): ofmsgs = [] max_prefixlen = faucet_vip.ip.max_prefixlen faucet_vip_host = self._host_from_faucet_vip(faucet_vip) priority = self.route_priority + max_prefixlen ofmsgs.append(self.valve_flowmod( self.eth_src_table, self.valve_in_match( self.eth_src_table, eth_type=ether.ETH_TYPE_ARP, nw_dst=faucet_vip_host, vlan=vlan), priority=priority, inst=[valve_of.apply_actions([valve_of.output_controller()])])) # Initialize IPv4 FIB ofmsgs.append(self.valve_flowmod( self.eth_src_table, self.valve_in_match( self.eth_src_table, eth_type=self._eth_type(), eth_dst=self.faucet_mac, vlan=vlan), priority=self.route_priority, inst=[valve_of.goto_table(self.fib_table)])) ofmsgs.append(self.valve_flowcontroller( self.fib_table, self.valve_in_match( self.fib_table, vlan=vlan, eth_type=self._eth_type(), nw_proto=inet.IPPROTO_ICMP, nw_src=faucet_vip, nw_dst=faucet_vip_host), priority=priority)) return ofmsgs
def _add_faucet_fib_to_vip(self, vlan, priority, faucet_vip, faucet_vip_host): learn_connected_priority = self.route_priority + faucet_vip.network.prefixlen faucet_mac = vlan.faucet_mac insts = [valve_of.goto_table(self.fib_table)] if self._global_routing(): vlan_mac = valve_packet.int_in_mac(faucet_mac, vlan.vid) insts = [valve_of.apply_actions([ valve_of.set_eth_dst(vlan_mac), valve_of.set_vlan_vid(self.global_vlan.vid)])] + insts ofmsgs = [] ofmsgs.append(self.eth_src_table.flowmod( self.eth_src_table.match(eth_type=self.ETH_TYPE, eth_dst=faucet_mac, vlan=vlan), priority=self.route_priority, inst=insts)) routed_vlans = self._routed_vlans(vlan) if self._global_routing(): vlan = self.global_vlan ofmsgs.append(self.fib_table.flowmod( self._route_match(vlan, faucet_vip_host), priority=priority, inst=[valve_of.goto_table(self.vip_table)])) if self.proactive_learn and not faucet_vip.ip.is_link_local: for routed_vlan in routed_vlans: ofmsgs.append(self.fib_table.flowmod( self._route_match(routed_vlan, faucet_vip), priority=learn_connected_priority, inst=[valve_of.goto_table(self.vip_table)])) ofmsgs.append(self.vip_table.flowcontroller( self.vip_table.match(eth_type=self.ETH_TYPE), priority=priority-1, max_len=self.MAX_LEN)) return ofmsgs
def build_tunnel_ofmsgs(rule_conf, acl_table, priority, port_num=None, vlan_vid=None, flowdel=False, reverse=False): """Build a specific tunnel only ofmsgs""" ofmsgs = [] acl_inst = [] acl_match = [] acl_match_dict = {} _, output_actions, output_ofmsgs, output_inst = build_output_actions(acl_table, rule_conf) ofmsgs.extend(output_ofmsgs) acl_inst.extend(output_inst) acl_inst.append(valve_of.apply_actions(output_actions)) if port_num is not None: acl_match_dict['in_port'] = port_num if vlan_vid is not None: acl_match_dict['vlan_vid'] = valve_of.vid_present(vlan_vid) if reverse: acl_match_dict['vlan_pcp'] = valve_of.PCP_TUNNEL_REVERSE_DIRECTION_FLAG else: acl_match_dict['vlan_pcp'] = valve_of.PCP_TUNNEL_FLAG try: acl_match = valve_of.match_from_dict(acl_match_dict) except TypeError as type_error: raise InvalidConfigError('invalid match type in ACL') from type_error flowmod = acl_table.flowmod(acl_match, priority=priority, inst=tuple(acl_inst)) if flowdel: ofmsgs.append(acl_table.flowdel(match=acl_match, priority=priority, strict=False)) ofmsgs.append(flowmod) return ofmsgs
def _build_flood_rule(self, match, command, flood_acts, flood_priority): return [ self.flood_table.flowmod(match=match, command=command, inst=[valve_of.apply_actions(flood_acts)], priority=flood_priority) ]
def create_mab_flow(self, port_num, nfv_sw_port_num, mac): """ Create MAB ACL for sending IP Activity to Chewie NFV Returns flowmods to send all IP traffic to Chewie Args: port_num (int): Number of port in nfv_sw_port_num(int): Number of port out mac(str): MAC address of the valve/port combo """ return self.port_acl_table.flowmod( match=self.port_acl_table.match( in_port=port_num, eth_type=valve_of.ether.ETH_TYPE_IP, nw_proto=valve_of.inet.IPPROTO_UDP, udp_src=68, udp_dst=67, ), priority=self.low_priority, inst=(valve_of.apply_actions([ self.port_acl_table.set_field(eth_dst=mac), valve_of.output_port(nfv_sw_port_num) ]), ), )
def _build_group_flood_rules(self, vlan, modify, command): """Build flooding rules for a VLAN using groups.""" flood_priority = self.flood_priority broadcast_group = self.groups.get_entry( vlan.vid, self._build_group_buckets(vlan, False)) unicast_group = self.groups.get_entry( vlan.vid + valve_of.VLAN_GROUP_OFFSET, self._build_group_buckets(vlan, vlan.unicast_flood)) ofmsgs = [] if modify: ofmsgs.append(broadcast_group.modify()) ofmsgs.append(unicast_group.modify()) else: ofmsgs.extend(broadcast_group.add()) ofmsgs.extend(unicast_group.add()) for unicast_eth_dst, eth_dst, eth_dst_mask in self.FLOOD_DSTS: if unicast_eth_dst and not vlan.unicast_flood: continue group = broadcast_group if not eth_dst: group = unicast_group match = self.flood_table.match(vlan=vlan, eth_dst=eth_dst, eth_dst_mask=eth_dst_mask) ofmsgs.append( self.flood_table.flowmod( match=match, command=command, inst=[ valve_of.apply_actions( [valve_of.group_act(group.group_id)]) ], priority=flood_priority)) flood_priority += 1 return ofmsgs
def _build_group_flood_rules(self, vlan, modify, command): """Build flooding rules for a VLAN using groups.""" flood_priority = self.flood_priority broadcast_group = self.groups.get_entry( vlan.vid, self._build_group_buckets(vlan, False)) unicast_group = self.groups.get_entry( vlan.vid + valve_of.VLAN_GROUP_OFFSET, self._build_group_buckets(vlan, vlan.unicast_flood)) ofmsgs = [] if modify: ofmsgs.append(broadcast_group.modify()) ofmsgs.append(unicast_group.modify()) else: ofmsgs.extend(broadcast_group.add()) ofmsgs.extend(unicast_group.add()) for unicast_eth_dst, eth_dst, eth_dst_mask in self.FLOOD_DSTS: if unicast_eth_dst and not vlan.unicast_flood: continue group = broadcast_group if not eth_dst: group = unicast_group match = self.flood_table.match( vlan=vlan, eth_dst=eth_dst, eth_dst_mask=eth_dst_mask) ofmsgs.append(self.flood_table.flowmod( match=match, command=command, inst=[valve_of.apply_actions([valve_of.group_act(group.group_id)])], priority=flood_priority)) flood_priority += 1 return ofmsgs
def output(self, port, vlan, hairpin=False, loop_protect_field=None): """Get instructions list to output a packet through the regular pipeline. args: port: Port object of port to output packet to vlan: Vlan object of vlan to output packet on hairpin: if True, hairpinning is required returns: list of Instructions """ instructions = [] if self.egress_table: metadata, metadata_mask = faucet_md.get_egress_metadata( port.number, vlan.vid) instructions.extend( valve_of.metadata_goto_table(metadata, metadata_mask, self.egress_table)) else: instructions.append( valve_of.apply_actions( vlan.output_port(port, hairpin=hairpin, output_table=self.output_table, loop_protect_field=loop_protect_field))) return instructions
def learn_host_on_vlan_port_flows(self, port, vlan, eth_src, delete_existing, refresh_rules, src_rule_idle_timeout, src_rule_hard_timeout, dst_rule_idle_timeout): """Return flows that implement learning a host on a port.""" ofmsgs = [] # Delete any existing entries for MAC. if delete_existing: ofmsgs.extend(self.delete_host_from_vlan(eth_src, vlan)) # Associate this MAC with source port. src_match = self.eth_src_table.match( in_port=port.number, vlan=vlan, eth_src=eth_src) src_priority = self.host_priority - 1 inst = self.eth_src_table.goto(self.output_table) if port.override_output_port: inst = valve_of.apply_actions([ valve_of.output_port(port.override_output_port.number)]) loop_protect_field = None if port.tagged_vlans and port.loop_protect_external and self.stack: loop_protect_field = 0 ofmsgs.append(self.eth_src_table.flowmod( match=src_match, priority=src_priority, inst=[inst], hard_timeout=src_rule_hard_timeout, idle_timeout=src_rule_idle_timeout)) hairpinning = port.hairpin or port.hairpin_unicast # If we are refreshing only and not in hairpin mode, leave existing eth_dst alone. if refresh_rules and not hairpinning: return ofmsgs # Output packets for this MAC to specified port. ofmsgs.append(self.eth_dst_table.flowmod( self.eth_dst_table.match(vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=self.pipeline.output(port, vlan, loop_protect_field=loop_protect_field), idle_timeout=dst_rule_idle_timeout)) # If port is in hairpin mode, install a special rule # that outputs packets destined to this MAC back out the same # port they came in (e.g. multiple hosts on same WiFi AP, # and FAUCET is switching between them on the same port). if hairpinning: ofmsgs.append(self.eth_dst_hairpin_table.flowmod( self.eth_dst_hairpin_table.match(in_port=port.number, vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=self.pipeline.output(port, vlan, hairpin=True), idle_timeout=dst_rule_idle_timeout)) return ofmsgs
def learn_host_on_vlan_port_flows(self, port, vlan, eth_src, delete_existing, refresh_rules, src_rule_idle_timeout, src_rule_hard_timeout, dst_rule_idle_timeout): """Return flows that implement learning a host on a port.""" ofmsgs = [] # Delete any existing entries for MAC. if delete_existing: ofmsgs.extend(self.delete_host_from_vlan(eth_src, vlan)) # Associate this MAC with source port. src_match = self.eth_src_table.match(in_port=port.number, vlan=vlan, eth_src=eth_src) src_priority = self.host_priority - 1 inst = self.eth_src_table.goto(self.output_table) if port.override_output_port: inst = valve_of.apply_actions( [valve_of.output_port(port.override_output_port.number)]) ofmsgs.append( self.eth_src_table.flowmod(match=src_match, priority=src_priority, inst=[inst], hard_timeout=src_rule_hard_timeout, idle_timeout=src_rule_idle_timeout)) hairpinning = port.hairpin or port.hairpin_unicast # If we are refreshing only and not in hairpin mode, leave existing eth_dst alone. if refresh_rules and not hairpinning: return ofmsgs # Output packets for this MAC to specified port. ofmsgs.append( self.eth_dst_table.flowmod(self.eth_dst_table.match( vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=self.pipeline.output(port, vlan), idle_timeout=dst_rule_idle_timeout)) # If port is in hairpin mode, install a special rule # that outputs packets destined to this MAC back out the same # port they came in (e.g. multiple hosts on same WiFi AP, # and FAUCET is switching between them on the same port). if hairpinning: ofmsgs.append( self.eth_dst_hairpin_table.flowmod( self.eth_dst_hairpin_table.match(in_port=port.number, vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=self.pipeline.output(port, vlan, hairpin=True), idle_timeout=dst_rule_idle_timeout)) return ofmsgs
def _port_add_vlan_untagged(self, port, vlan, forwarding_table, mirror_act): push_vlan_act = mirror_act + valve_of.push_vlan_act(vlan.vid) push_vlan_inst = [ valve_of.apply_actions(push_vlan_act), valve_of.goto_table(forwarding_table) ] null_vlan = namedtuple('null_vlan', 'vid') null_vlan.vid = valve_of.ofp.OFPVID_NONE return self._port_add_vlan_rules(port, null_vlan, push_vlan_inst)
def flowcontroller(self, match=None, priority=None, inst=None, max_len=96): """Add flow outputting to controller.""" if inst is None: inst = () return self.flowmod(match=match, priority=priority, inst=(valve_of.apply_actions( (valve_of.output_controller(max_len), )), ) + inst)
def flowcontroller(self, match=None, priority=None, inst=None, max_len=96): """Add flow outputting to controller.""" if inst is None: inst = [] return self.flowmod( match=match, priority=priority, inst=[valve_of.apply_actions( [valve_of.output_controller(max_len)])] + inst)
def _add_faucet_vip_nd(self, vlan, priority, faucet_vip, faucet_vip_host): faucet_vip_host_nd_mcast = valve_packet.ipv6_link_eth_mcast( valve_packet.ipv6_solicited_node_from_ucast(faucet_vip.ip)) controller_and_flood = [ valve_of.apply_actions([valve_of.output_controller()]), self.vip_table.goto(self.output_table) ] ofmsgs = [] ofmsgs.append( self.vip_table.flowcontroller(self.vip_table.match( eth_type=self.ETH_TYPE, nw_proto=valve_of.inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ICMPV6_ECHO_REQUEST), priority=priority, max_len=self.MAX_LEN)) # IPv6 ND for FAUCET VIP ofmsgs.append( self.eth_src_table.flowmod( self.eth_src_table.match(eth_type=self.ETH_TYPE, eth_dst=faucet_vip_host_nd_mcast, vlan=vlan), priority=priority, inst=[self.eth_src_table.goto(self.vip_table)])) ofmsgs.append( self.vip_table.flowmod(self.vip_table.match( eth_type=self.ETH_TYPE, nw_proto=valve_of.inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_NEIGHBOR_SOLICIT), priority=priority, inst=controller_and_flood)) # IPv6 ND for connected hosts. ofmsgs.append( self.vip_table.flowcontroller(self.vip_table.match( eth_type=self.ETH_TYPE, eth_dst=vlan.faucet_mac, nw_proto=valve_of.inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_NEIGHBOR_ADVERT), priority=priority, max_len=self.MAX_LEN)) if faucet_vip.ip.is_link_local: ofmsgs.append( self.eth_src_table.flowmod( self.eth_src_table.match( eth_type=self.ETH_TYPE, eth_dst=valve_packet.IPV6_ALL_ROUTERS_MCAST, vlan=vlan), priority=priority, inst=[self.eth_src_table.goto(self.vip_table)])) ofmsgs.append( self.vip_table.flowmod(self.vip_table.match( eth_type=self.ETH_TYPE, nw_proto=valve_of.inet.IPPROTO_ICMPV6, icmpv6_type=icmpv6.ND_ROUTER_SOLICIT), priority=priority, inst=controller_and_flood)) return ofmsgs
def select_packets(self, target_table, match_dict, actions=None, priority_offset=0): """retrieve rules to redirect packets matching match_dict to table""" inst = [target_table.goto_this()] if actions is not None: inst.append(valve_of.apply_actions(actions)) return [self.classification_table.flowmod( self.classification_table.match(**match_dict), priority=self.select_priority + priority_offset, inst=inst)]
def build_port_out_inst(self, vlan, port): dst_act = [] if not vlan.port_is_tagged(port.number) and port.stack is None: dst_act.append(valve_of.pop_vlan()) dst_act.append(valve_of.output_port(port.number)) if port.mirror is not None: mirror_acts = [valve_of.output_port(port.mirror)] dst_act.extend(mirror_acts) return [valve_of.apply_actions(dst_act)]
def _build_group_flood_rules(self, vlan, modify, command): """Build flooding rules for a VLAN using groups.""" ofmsgs = [] groups_by_unicast_eth = {} _, vlan_flood_acts = self._build_flood_rule_for_vlan( vlan, None, None, False, command) group_id = vlan.vid group = self.groups.get_entry( group_id, valve_of.build_group_flood_buckets(vlan_flood_acts)) groups_by_unicast_eth[False] = group groups_by_unicast_eth[True] = group # Only configure unicast flooding group if has different output # actions to non unicast flooding. _, unicast_eth_vlan_flood_acts = self._build_flood_rule_for_vlan( vlan, None, None, True, command) unicast_eth_vlan_flood_acts, unicast_output_ports, _ = self._output_non_output_actions( unicast_eth_vlan_flood_acts) vlan_flood_acts, vlan_output_ports, _ = self._output_non_output_actions( vlan_flood_acts) if unicast_output_ports != vlan_output_ports: group_id += valve_of.VLAN_GROUP_OFFSET group = self.groups.get_entry( group_id, valve_of.build_group_flood_buckets( unicast_eth_vlan_flood_acts)) groups_by_unicast_eth[True] = group for group in groups_by_unicast_eth.values(): if modify: ofmsgs.append(group.modify()) else: ofmsgs.extend(group.add()) for unicast_eth_dst, eth_dst, eth_dst_mask in self.FLOOD_DSTS: if unicast_eth_dst and not vlan.unicast_flood: continue group = groups_by_unicast_eth[unicast_eth_dst] match = self.flood_table.match(vlan=vlan, eth_dst=eth_dst, eth_dst_mask=eth_dst_mask) flood_priority = self._mask_flood_priority(eth_dst_mask) ofmsgs.append( self.flood_table.flowmod( match=match, command=command, inst=[ valve_of.apply_actions( [valve_of.group_act(group.group_id)]) ], priority=flood_priority)) return ofmsgs
def _add_egress_table_rule(self, port, vlan, pop_vlan=True): metadata, metadata_mask = faucet_md.get_egress_metadata( port.number, vlan.vid) actions = copy.copy(port.mirror_actions()) if pop_vlan: actions.append(valve_of.pop_vlan()) actions.append(valve_of.output_port(port.number)) inst = (valve_of.apply_actions(tuple(actions)), ) return self.egress_table.flowmod(self.egress_table.match( vlan=vlan, metadata=metadata, metadata_mask=metadata_mask), priority=self.dp.high_priority, inst=inst)
def accept_to_l2_forwarding(self, actions=None): """Get instructions to forward packet through the pipeline to l2 forwarding. args: actions: (optional) list of actions to apply to packet. returns: list of instructions """ inst = [self.output_table.goto_this()] if actions is not None: inst.append(valve_of.apply_actions(actions)) return inst
def add_port(self, port): """initialise override_output_port if necessary""" ofmsgs = [] if port.override_output_port: ofmsgs.append(self.eth_src_table.flowmod( match=self.eth_src_table.match( in_port=port.number), priority=self.low_priority + 1, inst=[valve_of.apply_actions([ valve_of.output_controller(), valve_of.output_port(port.override_output_port.number)])])) return ofmsgs
def _build_flood_rule_for_vlan(self, vlan, eth_dst, eth_dst_mask, # pylint: disable=too-many-arguments exclude_unicast, command, flood_priority, preflood_acts): ofmsgs = [] match = self.flood_table.match( vlan=vlan, eth_dst=eth_dst, eth_dst_mask=eth_dst_mask) flood_acts = self._build_flood_rule_actions( vlan, exclude_unicast, None) ofmsgs.append(self.flood_table.flowmod( match=match, command=command, inst=[valve_of.apply_actions(preflood_acts + flood_acts)], priority=flood_priority)) return ofmsgs
def _add_egress_table_rule(self, port, vlan, pop_vlan=True): metadata, metadata_mask = faucet_md.get_egress_metadata( port.number, vlan.vid) actions = copy.copy(port.mirror_actions()) if pop_vlan: actions.append(valve_of.pop_vlan()) actions.append(valve_of.output_port(port.number)) inst = [valve_of.apply_actions(actions)] return self.egress_table.flowmod( self.egress_table.match( vlan=vlan, metadata=metadata, metadata_mask=metadata_mask ), priority=self.dp.high_priority, inst=inst )
def _build_group_flood_rules(self, vlan, modify, command): """Build flooding rules for a VLAN using groups.""" ofmsgs = [] groups_by_unicast_eth = {} _, vlan_flood_acts = self._build_flood_rule_for_vlan( vlan, None, None, False, command) group_id = vlan.vid group = self.groups.get_entry( group_id, valve_of.build_group_flood_buckets(vlan_flood_acts)) groups_by_unicast_eth[False] = group groups_by_unicast_eth[True] = group # Only configure unicast flooding group if has different output # actions to non unicast flooding. _, unicast_eth_vlan_flood_acts = self._build_flood_rule_for_vlan( vlan, None, None, True, command) unicast_eth_vlan_flood_acts, unicast_output_ports, _ = self._output_non_output_actions( unicast_eth_vlan_flood_acts) vlan_flood_acts, vlan_output_ports, _ = self._output_non_output_actions(vlan_flood_acts) if unicast_output_ports != vlan_output_ports: group_id += valve_of.VLAN_GROUP_OFFSET group = self.groups.get_entry( group_id, valve_of.build_group_flood_buckets(unicast_eth_vlan_flood_acts)) groups_by_unicast_eth[True] = group for group in groups_by_unicast_eth.values(): if modify: ofmsgs.append(group.modify()) else: ofmsgs.extend(group.add()) for unicast_eth_dst, eth_dst, eth_dst_mask in self.FLOOD_DSTS: if unicast_eth_dst and not vlan.unicast_flood: continue group = groups_by_unicast_eth[unicast_eth_dst] match = self.flood_table.match( vlan=vlan, eth_dst=eth_dst, eth_dst_mask=eth_dst_mask) flood_priority = self._mask_flood_priority(eth_dst_mask) ofmsgs.append(self.flood_table.flowmod( match=match, command=command, inst=[valve_of.apply_actions([valve_of.group_act(group.group_id)])], priority=flood_priority)) return ofmsgs
def _add_resolved_route(self, vlan, ip_gw, ip_dst, eth_dst, is_updated): ofmsgs = [] if is_updated: self.logger.info( 'Updating next hop for route %s via %s (%s) on VLAN %u' % ( ip_dst, ip_gw, eth_dst, vlan.vid)) ofmsgs.extend(self._del_route_flows(vlan, ip_dst)) else: self.logger.info( 'Adding new route %s via %s (%s) on VLAN %u' % ( ip_dst, ip_gw, eth_dst, vlan.vid)) inst = [valve_of.apply_actions(self._nexthop_actions(eth_dst, vlan)), self.fib_table.goto(self.output_table)] routed_vlans = self._routed_vlans(vlan) for routed_vlan in routed_vlans: in_match = self._route_match(routed_vlan, ip_dst) ofmsgs.append(self.fib_table.flowmod( in_match, priority=self._route_priority(ip_dst), inst=inst)) return ofmsgs
def output(self, port, vlan, hairpin=False): """Get instructions list to output a packet through the regular pipeline. args: port: Port object of port to output packet to vlan: Vlan object of vlan to output packet on hairpin: if True, hairpinning is required returns: list of Instructions """ instructions = [] if self.egress_table: metadata, metadata_mask = faucet_md.get_egress_metadata( port.number, vlan.vid) instructions.extend(valve_of.metadata_goto_table( metadata, metadata_mask, self.egress_table)) else: instructions.append(valve_of.apply_actions(vlan.output_port(port, hairpin=hairpin))) return instructions
def _add_faucet_fib_to_vip(self, vlan, priority, faucet_vip, faucet_vip_host): ofmsgs = [] learn_connected_priority = self.route_priority + faucet_vip.network.prefixlen faucet_mac = vlan.faucet_mac insts = [self.classification_table.goto(self.fib_table)] if self.global_routing: vlan_mac = valve_packet.int_in_mac(faucet_mac, vlan.vid) insts = [valve_of.apply_actions([ self.fib_table.set_field(eth_dst=vlan_mac), self.fib_table.set_vlan_vid(self.global_vlan.vid)])] + insts ofmsgs.append(self.classification_table.flowmod( self.classification_table.match( eth_type=self.ETH_TYPE, eth_dst=faucet_mac, vlan=vlan), priority=self.route_priority, inst=insts)) if self.global_routing: vlan = self.global_vlan ofmsgs.append(self.fib_table.flowmod( self._route_match(vlan, faucet_vip_host), priority=priority, inst=[self.fib_table.goto(self.vip_table)])) if self.proactive_learn and not faucet_vip.ip.is_link_local: routed_vlans = self._routed_vlans(vlan) for routed_vlan in routed_vlans: ofmsgs.append(self.fib_table.flowmod( self._route_match(routed_vlan, faucet_vip), priority=learn_connected_priority, inst=[self.fib_table.goto(self.vip_table)])) # Unicast ICMP to us. priority -= 1 ofmsgs.append(self.vip_table.flowcontroller( self.vip_table.match( eth_type=self.ETH_TYPE, eth_dst=faucet_mac, nw_proto=self.ICMP_TYPE), priority=priority, max_len=self.ICMP_SIZE)) # Learn + flood other ICMP not unicast to us. priority -= 1 ofmsgs.append(self.vip_table.flowmod( self.vip_table.match( eth_type=self.ETH_TYPE, nw_proto=self.ICMP_TYPE), priority=priority, inst=self._controller_and_flood())) # Learn from other IP traffic unicast to us. priority -= 1 ofmsgs.append(self.vip_table.flowcontroller( self.vip_table.match( eth_type=self.ETH_TYPE, eth_dst=faucet_mac), priority=priority, max_len=self.ICMP_SIZE)) # Learn + flood IP traffic not unicast to us. priority -= 1 ofmsgs.append(self.vip_table.flowmod( self.vip_table.match( eth_type=self.ETH_TYPE), priority=priority, inst=self._controller_and_flood())) return ofmsgs
def _controller_and_flood(self): return [ valve_of.apply_actions([valve_of.output_controller(max_len=self.ICMP_SIZE)]), self.vip_table.goto(self.output_table)]
def learn_host_on_vlan_port_flows(self, port, vlan, eth_src, delete_existing, refresh_rules, src_rule_idle_timeout, src_rule_hard_timeout, dst_rule_idle_timeout): """Return flows that implement learning a host on a port.""" ofmsgs = [] if port.permanent_learn: # Antispoofing rule for this MAC. if self.classification_table != self.eth_src_table: ofmsgs.append(self.classification_table.flowmod( self.classification_table.match( in_port=port.number, vlan=vlan, eth_src=eth_src), priority=self.host_priority, inst=[self.classification_table.goto(self.eth_src_table)])) ofmsgs.append(self.classification_table.flowdrop( self.classification_table.match(vlan=vlan, eth_src=eth_src), priority=(self.host_priority - 2))) else: # Delete any existing entries for MAC. if delete_existing: ofmsgs.extend(self.delete_host_from_vlan(eth_src, vlan)) # Associate this MAC with source port. src_match = self.eth_src_table.match( in_port=port.number, vlan=vlan, eth_src=eth_src) src_priority = self.host_priority - 1 inst = self.eth_src_table.goto(self.output_table) if port.override_output_port: inst = valve_of.apply_actions([ valve_of.output_port(port.override_output_port.number)]) ofmsgs.append(self.eth_src_table.flowmod( match=src_match, priority=src_priority, inst=[inst], hard_timeout=src_rule_hard_timeout, idle_timeout=src_rule_idle_timeout)) hairpinning = port.hairpin or port.hairpin_unicast # If we are refreshing only and not in hairpin mode, leave existing eth_dst alone. if refresh_rules and not hairpinning: return ofmsgs # Output packets for this MAC to specified port. if self.egress_table is not None: metadata, metadata_mask = get_egress_metadata(port.number, vlan.vid) ofmsgs.append(self.eth_dst_table.flowmod( self.eth_dst_table.match(vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=valve_of.metadata_goto_table( metadata, metadata_mask, self.egress_table), idle_timeout=dst_rule_idle_timeout)) else: ofmsgs.append(self.eth_dst_table.flowmod( self.eth_dst_table.match(vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=[valve_of.apply_actions(vlan.output_port(port))], idle_timeout=dst_rule_idle_timeout)) # If port is in hairpin mode, install a special rule # that outputs packets destined to this MAC back out the same # port they came in (e.g. multiple hosts on same WiFi AP, # and FAUCET is switching between them on the same port). if hairpinning: ofmsgs.append(self.eth_dst_hairpin_table.flowmod( self.eth_dst_hairpin_table.match(in_port=port.number, vlan=vlan, eth_dst=eth_src), priority=self.host_priority, inst=[valve_of.apply_actions(vlan.output_port(port, hairpin=True))], idle_timeout=dst_rule_idle_timeout)) return ofmsgs
def build_acl_entry(acl_table, rule_conf, meters, acl_allow_inst, acl_force_port_vlan_inst, port_num=None, vlan_vid=None): """Build flow/groupmods for one ACL rule entry.""" acl_inst = [] acl_act = [] acl_match_dict = {} acl_ofmsgs = [] acl_cookie = None allow_inst = acl_allow_inst for attrib, attrib_value in rule_conf.items(): if attrib == 'in_port': continue if attrib == 'cookie': acl_cookie = attrib_value continue if attrib == 'description': continue if attrib == 'actions': allow = False allow_specified = False if 'allow' in attrib_value: allow_specified = True if attrib_value['allow'] == 1: allow = True if 'force_port_vlan' in attrib_value: if attrib_value['force_port_vlan'] == 1: allow_inst = acl_force_port_vlan_inst if 'meter' in attrib_value: meter_name = attrib_value['meter'] acl_inst.append(valve_of.apply_meter(meters[meter_name].meter_id)) if 'mirror' in attrib_value: port_no = attrib_value['mirror'] acl_act.append(valve_of.output_port(port_no)) if not allow_specified: allow = True if 'output' in attrib_value: output_port, output_actions, output_ofmsgs = build_output_actions( acl_table, attrib_value['output']) acl_act.extend(output_actions) acl_ofmsgs.extend(output_ofmsgs) # if port specified, output packet now and exit pipeline. if not allow and output_port is not None: continue if allow: acl_inst.extend(allow_inst) else: acl_match_dict[attrib] = attrib_value if port_num is not None: acl_match_dict['in_port'] = port_num if vlan_vid is not None: acl_match_dict['vlan_vid'] = valve_of.vid_present(vlan_vid) try: acl_match = valve_of.match_from_dict(acl_match_dict) except TypeError: raise InvalidConfigError('invalid type in ACL') if acl_act: acl_inst.append(valve_of.apply_actions(acl_act)) return (acl_match, acl_inst, acl_cookie, acl_ofmsgs)
def _build_flood_rule(self, match, command, flood_acts, flood_priority): return self.flood_table.flowmod( match=match, command=command, inst=[valve_of.apply_actions(flood_acts)], priority=flood_priority)
def _accept_to_table(self, table, actions): inst = [table.goto_this()] if actions is not None: inst.append(valve_of.apply_actions(actions)) return inst