def __init__(self, *args, **kwargs): VPNInstanceDataplane.__init__(self, *args) self.arpNetNS = ("%s%d" % (ARPNETNS_PREFIX, self.instanceId))[:LINUX_DEV_LEN] # Initialize dict where we store info on OVS ports (port numbers and # bound IP address) self._ovsPortInfo = dict() # Find ethX MPLS interface MAC address if not self.driver.useGRE: self.mplsIfMacAddress = net_utils.get_device_mac( self._runCommand, self.driver.mpls_interface) else: self.mplsIfMacAddress = None self.bridge = self.driver.bridge self.fallback = None self.push_vlan_action = None if self.driver.proxy_arp: self._initARPNetNS() # Create VRF-specific OVS patch ports self.log.debug( "Creating VRF patch ports and mapping traffic to gateway...") self.patchPortIn = 'ipvpn%d-pp-in' % self.instanceId self.patchPortOut = 'ipvpn%d-pp-out' % self.instanceId self._runCommand("ovs-vsctl --may-exist add-port %s %s -- " "set Interface %s type=patch options:peer=%s" % (self.bridge, self.patchPortIn, self.patchPortIn, self.patchPortOut)) self._runCommand("ovs-vsctl --may-exist add-port %s %s -- " "set Interface %s type=patch options:peer=%s" % (self.bridge, self.patchPortOut, self.patchPortOut, self.patchPortIn)) self.patchPortInNumber = self.driver.find_ovs_port(self.patchPortIn) self.patchPortOutNumber = self.driver.find_ovs_port(self.patchPortOut) if self.driver.proxy_arp: # Map traffic from patch port to gateway self._ovs_flow_add( 'in_port=%s,ip,nw_dst=%s' % (self.patchPortInNumber, self.gatewayIP), 'output:%s' % self.arpNetNSPort, self.driver.ovs_table_vrfs)
def main(): usage = "usage: %prog [--attach|--detach] --network-type (ipvpn|evpn) "\ "--port (<port>|netns) --ip <ip>[/<mask>] [options] (see --help)" parser = OptionParser(usage) parser.add_option("--attach", dest="operation", action="store_const", const="attach", help="attach local port") parser.add_option("--detach", dest="operation", action="store_const", const="detach", help="detach local port") parser.add_option("--network-type", dest="network_type", help="network type (ipvpn or evpn)", choices=["ipvpn", "evpn"]) parser.add_option("--vpn-instance-id", dest="vpn_instance_id", help="UUID for the network instance " "(default: %default-(ipvpn|evpn))", default=DEFAULT_VPN_INSTANCE_ID) parser.add_option("--port", dest="port", help="local port to attach/detach (use special port " "'netns[:if]' to have an interface to a local network " "namespace attached/detached " "[with 'if' as the name of the interface to the netns]") parser.add_option("--rt", dest="routeTargets", help="route target [default: 64512:0] (can be " "specified multiple times)", default=[], action="append") parser.add_option("--import-rt", dest="importOnlyRouteTargets", help="import-only route target (can be specified" "multiple times)", default=[], action="append") parser.add_option("--export-rt", dest="exportOnlyRouteTargets", help="export-only route target (can be specified" "multiple times)", default=[], action="append") parser.add_option("--ip", dest="ip", help="IP prefix / mask (mask defaults to /24)") parser.add_option("--gateway-ip", dest="gw_ip", help="IP address of network gateway (optional, " "defaults to last IP in range)") parser.add_option("--mac", dest="mac", help="MAC address (required for evpn if port" " is not 'netns')") parser.set_defaults(advertiseSubnet=False) parser.add_option("--advertise-singleton", action="store_false", dest="advertiseSubnet", help="advertise IP address as a /32 (default)") parser.add_option("--advertise-subnet", action="store_true", dest="advertiseSubnet", help="advertise the whole IP subnet") parser.add_option("--ovs-preplug", action="store_true", dest="ovs_preplug", default=False, help="should we prealably plug the port " "into an OVS bridge") parser.add_option("--ovs-bridge", dest="bridge", default="br-int", help="if preplug, specifies which OVS bridge to use" " (default: %default)") parser.add_option("--ovs-vlan", dest="ovs_vlan", type='int', help="if specified, only this VLAN from the OVS " "interface will be attached to the VPN instance " "(optional)") parser.add_option("--netns", dest="netns", help="name of network namespace (optional, for use with" " --port netns)") parser.add_option("--if2vpn", dest="if2vpn", default=NS2VPN_DEFAULT_IFNAME, help="name of interface in netns toward VPN" "defaults to %default " "(optional, for use with --port netns)") parser.add_option("--readv-from-rt", dest="reAdvFromRTs", help="enables route readvertisement from these RTs," " works in conjunction with --readv-to-rt", default=[], action="append") parser.add_option("--readv-to-rt", dest="reAdvToRTs", help="enables route readvertisement to these RTs," " works in conjunction with --readv-from-rt", default=[], action="append") (options, _) = parser.parse_args() if not(options.operation): parser.error("Need to specify --attach or --detach") if not(options.port): parser.error("Need to specify --port <localport>") if not(options.network_type): parser.error("Need to specify --network-type") if not(options.ip): parser.error("Need to specify --ip") if (len(options.routeTargets) == 0 and not (options.importOnlyRouteTargets or options.exportOnlyRouteTargets)): if options.network_type == "ipvpn": options.routeTargets = ["64512:512"] else: options.routeTargets = ["64512:513"] importRTs = copy(options.routeTargets or []) for rt in options.importOnlyRouteTargets: importRTs.append(rt) exportRTs = copy(options.routeTargets or []) for rt in options.exportOnlyRouteTargets: exportRTs.append(rt) if not re.match('.*/[0-9]+$', options.ip): options.ip = options.ip + "/24" if not(options.gw_ip): net = IPNetwork(options.ip) print "using %s as gateway address" % str(net[-2]) options.gw_ip = str(net[-2]) if options.vpn_instance_id == DEFAULT_VPN_INSTANCE_ID: options.vpn_instance_id = "%s-%s" % ( options.network_type, options.vpn_instance_id) if options.port.startswith("netns"): if not options.netns: options.netns = options.vpn_instance_id try: (_, options.if2netns) = options.port.split(":") except: options.if2netns = get_vpn2ns_if_name(options.netns) if options.operation == "attach": createSpecialNetNSPort(options) options.port = options.if2netns options.mac = get_device_mac(lambda *args: runCommand(log, *args), options.if2vpn, options.netns) print "Local port: %s (%s)" % (options.port, options.mac) runCommand(log, "ip link show %s" % options.port) local_port = {} if options.port[:5] == "evpn:": if (options.network_type == "ipvpn"): print "will plug evpn %s into the IPVPN" % options.port[5:] local_port['evpn'] = {'id': options.port[5:]} else: raise Exception("Can only plug an evpn into an ipvpn") else: local_port['linuxif'] = options.port # currently our only the MPLS OVS driver for ipvpn requires preplug if (options.ovs_preplug and options.network_type == "ipvpn"): print "pre-plugging %s into %s" % (options.port, options.bridge) runCommand(log, "ovs-vsctl del-port %s %s" % (options.bridge, options.port), raiseExceptionOnError=False) runCommand(log, "ovs-vsctl add-port %s %s" % (options.bridge, options.port)) local_port['ovs'] = {'port_name': options.port, 'plugged': True} if options.ovs_vlan: local_port['ovs']['vlan'] = options.ovs_vlan if not(options.mac): if options.network_type == "ipvpn": options.mac = "52:54:00:99:99:22" else: parser.error("Need to specify --mac for an EVPN network " "attachment if port is not 'netns'") readvertise = None if options.reAdvToRTs: readvertise = {"from_rt": options.reAdvFromRTs, "to_rt": options.reAdvToRTs} json_data = json.dumps({"import_rt": importRTs, "export_rt": exportRTs, "local_port": local_port, "vpn_instance_id": options.vpn_instance_id, "vpn_type": options.network_type, "gateway_ip": options.gw_ip, "mac_address": options.mac, "ip_address": options.ip, "advertise_subnet": options.advertiseSubnet, "readvertise": readvertise }) print "request: %s" % json_data os.environ['NO_PROXY'] = "127.0.0.1" req = urllib2.Request("http://127.0.0.1:8082/%s_localport" % options.operation, json_data, {'Content-Type': 'application/json'}) try: response = urllib2.urlopen(req) response_content = response.read() response.close() print "response: %d %s" % (response.getcode(), response_content) except urllib2.HTTPError as e: error_content = e.read() print " %s" % error_content sys.exit("error %d, reason: %s" % (e.code, e.reason))
def main(): usage = "usage: %prog [--attach|--detach] --network-type (ipvpn|evpn) "\ "--port (<port>|netns) --ip <ip>[/<mask>] [options] (see --help)" parser = OptionParser(usage) parser.add_option("--attach", dest="operation", action="store_const", const="attach", help="attach local port") parser.add_option("--detach", dest="operation", action="store_const", const="detach", help="detach local port") parser.add_option("--network-type", dest="network_type", help="network type (ipvpn or evpn)", choices=["ipvpn", "evpn"]) parser.add_option("--vpn-instance-id", dest="vpn_instance_id", help="UUID for the network instance " "(default: %default-(ipvpn|evpn))", default=DEFAULT_VPN_INSTANCE_ID) parser.add_option("--port", dest="port", help="local port to attach/detach (use special port " "'netns[:if]' to have an interface to a local network " "namespace attached/detached " "[with 'if' as the name of the interface to the netns]") parser.add_option("--rt", dest="routeTargets", help="route target [default: 64512:0] (can be " "specified multiple times)", default=[], action="append") parser.add_option("--import-rt", dest="importOnlyRouteTargets", help="import-only route target (can be specified" "multiple times)", default=[], action="append") parser.add_option("--export-rt", dest="exportOnlyRouteTargets", help="export-only route target (can be specified" "multiple times)", default=[], action="append") parser.add_option("--ip", dest="ip", help="IP prefix / mask (mask defaults to /24)") parser.add_option("--gateway-ip", dest="gw_ip", help="IP address of network gateway (optional, " "defaults to last IP in range)") parser.add_option("--mac", dest="mac", help="MAC address (required for evpn if port" " is not 'netns')") parser.set_defaults(advertiseSubnet=False) parser.add_option("--advertise-singleton", action="store_false", dest="advertiseSubnet", help="advertise IP address as a /32 (default)") parser.add_option("--advertise-subnet", action="store_true", dest="advertiseSubnet", help="advertise the whole IP subnet") parser.add_option("--ovs-preplug", action="store_true", dest="ovs_preplug", default=False, help="should we prealably plug the port " "into an OVS bridge") parser.add_option("--ovs-bridge", dest="bridge", default="br-int", help="if preplug, specifies which OVS bridge to use" " (default: %default)") parser.add_option("--ovs-vlan", dest="ovs_vlan", type='int', help="if specified, only this VLAN from the OVS " "interface will be attached to the VPN instance " "(optional)") parser.add_option("--netns", dest="netns", help="name of network namespace (optional, for use with" " --port netns)") parser.add_option("--if2vpn", dest="if2vpn", default=NS2VPN_DEFAULT_IFNAME, help="name of interface in netns toward VPN" "defaults to %default " "(optional, for use with --port netns)") parser.add_option("--readv-from-rt", dest="reAdvFromRTs", help="enables route readvertisement from these RTs," " works in conjunction with --readv-to-rt", default=[], action="append") parser.add_option("--readv-to-rt", dest="reAdvToRTs", help="enables route readvertisement to these RTs," " works in conjunction with --readv-from-rt", default=[], action="append") (options, _) = parser.parse_args() if not (options.operation): parser.error("Need to specify --attach or --detach") if not (options.port): parser.error("Need to specify --port <localport>") if not (options.network_type): parser.error("Need to specify --network-type") if not (options.ip): parser.error("Need to specify --ip") if (len(options.routeTargets) == 0 and not (options.importOnlyRouteTargets or options.exportOnlyRouteTargets)): if options.network_type == "ipvpn": options.routeTargets = ["64512:512"] else: options.routeTargets = ["64512:513"] importRTs = copy(options.routeTargets or []) for rt in options.importOnlyRouteTargets: importRTs.append(rt) exportRTs = copy(options.routeTargets or []) for rt in options.exportOnlyRouteTargets: exportRTs.append(rt) if not re.match('.*/[0-9]+$', options.ip): options.ip = options.ip + "/24" if not (options.gw_ip): net = IPNetwork(options.ip) print "using %s as gateway address" % str(net[-2]) options.gw_ip = str(net[-2]) if options.vpn_instance_id == DEFAULT_VPN_INSTANCE_ID: options.vpn_instance_id = "%s-%s" % (options.network_type, options.vpn_instance_id) if options.port.startswith("netns"): if not options.netns: options.netns = options.vpn_instance_id try: (_, options.if2netns) = options.port.split(":") except: options.if2netns = get_vpn2ns_if_name(options.netns) if options.operation == "attach": createSpecialNetNSPort(options) options.port = options.if2netns options.mac = get_device_mac(lambda *args: runCommand(log, *args), options.if2vpn, options.netns) print "Local port: %s (%s)" % (options.port, options.mac) runCommand(log, "ip link show %s" % options.port) local_port = {} if options.port[:5] == "evpn:": if (options.network_type == "ipvpn"): print "will plug evpn %s into the IPVPN" % options.port[5:] local_port['evpn'] = {'id': options.port[5:]} else: raise Exception("Can only plug an evpn into an ipvpn") else: local_port['linuxif'] = options.port # currently our only the MPLS OVS driver for ipvpn requires preplug if (options.ovs_preplug and options.network_type == "ipvpn"): print "pre-plugging %s into %s" % (options.port, options.bridge) runCommand(log, "ovs-vsctl del-port %s %s" % (options.bridge, options.port), raiseExceptionOnError=False) runCommand( log, "ovs-vsctl add-port %s %s" % (options.bridge, options.port)) local_port['ovs'] = {'port_name': options.port, 'plugged': True} if options.ovs_vlan: local_port['ovs']['vlan'] = options.ovs_vlan if not (options.mac): if options.network_type == "ipvpn": options.mac = "52:54:00:99:99:22" else: parser.error("Need to specify --mac for an EVPN network " "attachment if port is not 'netns'") readvertise = None if options.reAdvToRTs: readvertise = { "from_rt": options.reAdvFromRTs, "to_rt": options.reAdvToRTs } json_data = json.dumps({ "import_rt": importRTs, "export_rt": exportRTs, "local_port": local_port, "vpn_instance_id": options.vpn_instance_id, "vpn_type": options.network_type, "gateway_ip": options.gw_ip, "mac_address": options.mac, "ip_address": options.ip, "advertise_subnet": options.advertiseSubnet, "readvertise": readvertise }) print "request: %s" % json_data os.environ['NO_PROXY'] = "127.0.0.1" req = urllib2.Request( "http://127.0.0.1:8082/%s_localport" % options.operation, json_data, {'Content-Type': 'application/json'}) try: response = urllib2.urlopen(req) response_content = response.read() response.close() print "response: %d %s" % (response.getcode(), response_content) except urllib2.HTTPError as e: error_content = e.read() print " %s" % error_content sys.exit("error %d, reason: %s" % (e.code, e.reason))
def __init__(self, *args, **kwargs): VPNInstanceDataplane.__init__(self, *args) self.arpNetNS = ("%s-vrf%d" % (ARPNETNS_PREFIX, self.instanceId))[:LINUX_DEV_LEN] # Initialize dict where we store info on OVS ports (port numbers and # bound IP address) self._ovsPortInfo = dict() # Find ethX MPLS interface MAC address if not self.driver.useGRE: self.mplsIfMacAddress = get_device_mac( self._runCommand, self.driver.mpls_interface) else: self.mplsIfMacAddress = None self.bridge = self.driver.bridge self.log.info("VRF %d: Initializing network namespace %s for ARP " "proxing", self.instanceId, self.arpNetNS) # Get names of veth pair devices between OVS and network namespace ovsbr_to_proxyarp_ns = self.driver.get_ovsbr2arpns_if( self.arpNetNS) if not self._arpNetNsExists(): self.log.debug("VRF network namespace doesn't exist, creating...") # Create network namespace self._runCommand("ip netns add %s" % self.arpNetNS) # Set up veth pair devices between OVS and ARP network namespace self._create_arpnetns_veth_pair(ovsbr_to_proxyarp_ns, PROXYARP2OVS_IF) # Retrieve broadcast IP address ip = IPNetwork("%s/%s" % (self.gatewayIP, self.mask)) broadcastIP = str(ip.broadcast) # Set up network namespace interface as gateway self._runCommand("ip netns exec %s ip addr add %s/%s broadcast %s" " dev %s" % (self.arpNetNS, self.gatewayIP, self.mask, broadcastIP, PROXYARP2OVS_IF), raiseExceptionOnError=True) # Setup IP forwarding self._runCommand("ip netns exec %s sh -c \"echo 1 > /proc/sys" "/net/ipv4/ip_forward\"" % self.arpNetNS) self._runCommand("ip netns exec %s sh -c \"echo 1 > /proc/sys/net" "/ipv4/conf/all/forwarding\"" % self.arpNetNS) # Setup ARP proxying self._runCommand("ip netns exec %s sh -c \"echo 1 > /proc/sys/net" "/ipv4/conf/%s/proxy_arp\"" % (self.arpNetNS, PROXYARP2OVS_IF)) self._runCommand("ip netns exec %s sh -c \"echo 1 > /proc/sys/net" "/ipv4/conf/%s/proxy_arp_pvlan\"" % (self.arpNetNS, PROXYARP2OVS_IF)) else: self.log.debug("VRF network namespace already exists...") # OVS port number for the port toward the proxy ARP netns self.arpNetNSPort = self.driver.find_ovs_port(ovsbr_to_proxyarp_ns) # Find gateway ("network namespace to OVS" port) MAC address self.gwMacAddress = get_device_mac(self._runCommand, PROXYARP2OVS_IF, self.arpNetNS) # Create OVS patch ports self.log.debug( "Creating VRF patch ports and mapping traffic to gateway...") self.patchPortIn = 'ipvpn%d-pp-in' % self.instanceId self.patchPortOut = 'ipvpn%d-pp-out' % self.instanceId self._runCommand("ovs-vsctl --may-exist add-port %s %s -- " "set Interface %s type=patch options:peer=%s" % (self.bridge, self.patchPortIn, self.patchPortIn, self.patchPortOut)) self._runCommand("ovs-vsctl --may-exist add-port %s %s -- " "set Interface %s type=patch options:peer=%s" % (self.bridge, self.patchPortOut, self.patchPortOut, self.patchPortIn)) self.patchPortInNumber = self.driver.find_ovs_port(self.patchPortIn) self.patchPortOutNumber = self.driver.find_ovs_port(self.patchPortOut) # Map traffic from patch port to gateway self._ovs_flow_add('in_port=%s,ip,nw_dst=%s' % (self.patchPortInNumber, self.gatewayIP), 'output:%s' % self.arpNetNSPort, self.driver.ovs_table_vrfs)