Hi Min-Hyup, I was looking into something really similar a while back, and did write a controller that would do something similar. The controller was intended mainly for testing. I tried to remove as much of the special code as I could. I don't think the code will compile as it is now, but it might give you an idea of how you can go about implementing this.
I have been trying various things with VLANs, so if you have any further questions I might be able to help. --niky Min-Hyup KANG wrote: > > Hi all, > > > I am currently having several Pronto 3290 switches. > > > so, I would link to devide and assign some VLAN , such as VLAN2, VLAN3 > for some port. > > > I believe that I should write some code about vlan in NOX controller. > > but, It is not easy for me. so I need your help. > > > Can you explain easily how to write some code for vlan assignment. > > or > > please let me know example code showing vlan assignment. > > > Any suggestion would help to me. > > > and I am looking forward response. > > > Thanks, > > > > > Best Regards, > Min-Hyup KANG > > <mailto:kang-min-h...@hanmail.net> > ------------------------------------------------------------------------ > > _______________________________________________ > nox-dev mailing list > nox-dev@noxrepo.org > http://noxrepo.org/mailman/listinfo/nox-dev >
#Copyright (c) 2010 Raytheon BBN Technologies # # #Permission is hereby granted, free of charge, to any person obtaining # #a copy of this software and/or hardware specification (the "Work") to # #deal in the Work without restriction, including without limitation the # #rights to use, copy, modify, merge, publish, distribute, sublicense, # #and/or sell copies of the Work, and to permit persons to whom the Work # #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 Work. # # # #THE WORK 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 # #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # #HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # #WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # #OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS # #IN THE WORK. # This file is a modification of the discovery module of NOX. # This file also needs to be part of NOX in order to be compiled and used # # The release/copyright for the NOX software follows. # # Copyright 2008 (C) Nicira, Inc. # # NOX is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # NOX is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with NOX. If not, see <http://www.gnu.org/licenses/>. # import nox.lib.openflow as openflow from nox.lib.core import * from nox.lib.packet.ethernet import ethernet from nox.lib.packet.vlan import vlan from nox.lib.packet.ipv4 import ipv4 from nox.lib.packet.packet_utils import mac_to_str, mac_to_int, ethtype_to_str from twisted.python import log from nox.lib.packet.packet_utils import * import logging from time import time from socket import htons from struct import unpack logger = logging.getLogger('nox.coreapps.examples.vlanswitch') # Global vlanswitch instance inst = None # The actual physical port number of a switch doesn't necessarily correspond to the # port number that is advertized and passed along with the packets. It seems that the # physical port is what corresponds to the name field of the stats that are advertized # when a switch registers, keep a map between port names and numbers so that we can # do the switching port_name2no_map = {} port_no2name_map = {} # Timeout for cached MAC entries IDLE_CACHE_TIMEOUT = 5 HARD_CACHE_TIMEOUT = 60 #Switch statistics switch_stats = {} vlan_conf = { # <SWITCH_DPID> should be the dpid of the switch that this # configuration corresponds to <SWITCH_DPID> : { "port_to_vlans" : { '1' : ('a', [1714]), '2' : ('a', [1714]), '47' : ('t', [1713, 1714]), }, }, } # -- # If we've learned the destination MAC set up a flow and # send only out of its inport. Else, flood. # also check whether there is a need to set the vlan on the # packet # -- def forward_packet(dpid, inport, packet, buf, bufid): global inst, port_name2no_map, port_no2name_map if not port_name2no_map.has_key(dpid) or not port_no2name_map.has_key(dpid) : logger.warn("DPID %x doesn't have entry in the port maps" % dpid) return if not port_no2name_map[dpid].has_key(inport) : logger.warn('Pkt came from unregistered port %d. Drop packet!' % inport) # We are dropping the packet so return true since a decision about routing # has been made return inport_name = port_no2name_map[dpid][inport] logger.debug('Inport %s' % inport_name) if not vlan_conf.has_key(dpid) : logger.info("Don't have vlan conf for DPID 0x%x, do norma l2 forwarding" % dpid) forward_l2_packet(dpid, inport, packet, buf, bufid) return if not vlan_conf[dpid]['port_to_vlans'].has_key(inport_name) : logger.warn("DPID %x doesn't have vlan configuration for port %s. Drop packet" % (dpid, inport_name)) return dstaddr = packet.dst.tostring() actions = [] inport_vlan_conf = vlan_conf[dpid]['port_to_vlans'][inport_name] # Initialize the packet vlan to -1 pkt_vlan_tag = -1 # Find out if this is packet is taged and print the vlan information if packet.type == ethernet.VLAN_TYPE : if inport_vlan_conf[0] != 't' : logger.warn("VLAN tagged packet received on a non trunk port! Drop packet" ) return vlanh = packet.find('vlan') if vlanh == None : logger.warn("Packet type is VLAN_TYPE, but no vlan headers are present! Drop packet" ) return pkt_vlan_tag = vlanh.id # Keep track of dl_type pkt_dl_type = vlanh.eth_type logger.debug('vlan headers %s' % str(vlanh)) else : if inport_vlan_conf[0] != 'a' : logger.warn("Untagged packet received on a non access port! Drop packet" ) return logger.debug('packet is not tagged' ) pkt_vlan_tag = inport_vlan_conf[1][0] pkt_dl_type = packet.type # <GET_OUTPORT> : A function that will return the outport for this packet, the # outport is the OpenFlow outport, i.e. not the port name outport = <GET_OUTPORT> # if we are just broadcasting then just send this packet if outport == openflow.OFPP_FLOOD : logger.debug("Packet should be flooded") flood_packet(dpid, inport, packet, buf, bufid, pkt_vlan_tag) return # if we have a specific port, install a flow else : # Get the outport configuration if not port_no2name_map[dpid].has_key(outport) : logger.warn('Pkt for unregistered port %d. Drop packet!' % outport) return outport_name = port_no2name_map[dpid][outport] logger.debug('Outport %s' % outport_name) outport_vlan_conf = vlan_conf[dpid]['port_to_vlans'][outport_name] # If vlan of the packet is not in the port configuration if pkt_vlan_tag not in outport_vlan_conf[1] : logger.warn('Vlan of outport(%s), does not match pkt vlan (%d)' % (str(outport_vlan_conf), pkt_vlan_tad)) #drop packet return if outport_vlan_conf[0] == 'a' and packet.type == ethernet.VLAN_TYPE : # if the packet is tagged and the output port is access, remove the tag actions.append([openflow.OFPAT_STRIP_VLAN]) elif outport_vlan_conf[0] == 't' and packet.type != ethernet.VLAN_TYPE : # if the packet is not tagged and the output port is trunk, add the tag actions.append([openflow.OFPAT_SET_VLAN_VID, pkt_vlan_tag]) # Add the action of sending the packet to the computed outport actions.append([openflow.OFPAT_OUTPUT, [0, outport]]) logger.debug("ACTIONS : %s" % str(actions)) flow = extract_flow(packet) logger.info("%s : Install flow %s" % (str(time()), str(flow))) inst.install_datapath_flow(dpid, flow, IDLE_CACHE_TIMEOUT, HARD_CACHE_TIMEOUT, actions, bufid, openflow.OFP_DEFAULT_PRIORITY, inport, buf) return def flood_packet(dpid, inport, packet, buf, bufid, pkt_vlan_tag) : (access,trunk) = get_active_ports_on_vlan(dpid, pkt_vlan_tag) # remove inport access = set(access) - set([inport]) trunk = set(trunk) - set([inport]) access_actions = [] trunk_actions = [] # If the packet is tagged strip the tag for access ports if packet.type == ethernet.VLAN_TYPE : access_actions.append([openflow.OFPAT_STRIP_VLAN]) # If the packet is not tagged add a tag for trunk ports else : trunk_actions.append([openflow.OFPAT_SET_VLAN_VID, pkt_vlan_tag]) flow = extract_flow(packet) if len(access) > 0 : for p in access : access_actions.append([openflow.OFPAT_OUTPUT, [0, p]]) logger.debug("Flooding packet in access ports, actions %s" % str(access_actions)) inst.install_datapath_flow(dpid, flow, IDLE_CACHE_TIMEOUT, openflow.OFP_FLOW_PERMANENT, access_actions, bufid, openflow.OFP_DEFAULT_PRIORITY, inport, buf) #inst.send_openflow(dpid, bufid, buf, access_actions, inport) if len(trunk) > 0 : for p in trunk : trunk_actions.append([openflow.OFPAT_OUTPUT, [0, p]]) logger.debug("Flooding packet in trunk ports, actions %s" % str(trunk_actions)) inst.install_datapath_flow(dpid, flow, IDLE_CACHE_TIMEOUT, openflow.OFP_FLOW_PERMANENT, trunk_actions, bufid, openflow.OFP_DEFAULT_PRIORITY, inport, buf) #inst.send_openflow(dpid, None, buf, trunk_actions, inport) # This function takes as parameter a dpid and the specific vlan tag # and it returns a tuple of two lists of access and trunk ports # that are on that vlan. This function will return only ports that # are currently active and are also part of the configuration # Active ports are those that the switch has currently advertised to the # controller, and ports of the configuration are those defined in vlan_conf # The function returns the number of the ports as advertised by the switch # INPUT : # * dpid : the dpid of the switch we care about # * vlan_id : the id of the vlan we want to get the ports on # OUTPUT : # * (access, trunk) : where # - access : a list of access ports on VLAN vlan_id # - trunk : a list of trunk ports carring VLAN vlan_id # The fxn gets the configuration from vlan_conf def get_active_ports_on_vlan(dpid, vlan_id) : logger.debug("Get active ports for switch 0x%x on vlan %d" %(dpid, vlan_id)) access = [] trunk = [] if not vlan_conf.has_key(dpid) : logger.info("Don't have vlan conf for DPID 0x%x, return empty lists" % dpid) return (access, trunk) for p in vlan_conf[dpid]['port_to_vlans'].keys() : p_conf = vlan_conf[dpid]['port_to_vlans'][p] if vlan_id in p_conf[1] : # check if the switch has advirtised if port_name2no_map.has_key(dpid) and port_name2no_map[dpid].has_key(p) : p_no = port_name2no_map[dpid][p] if p_conf[0] == 'a' : access.append(p_no) else : trunk.append(p_no) logger.debug("access %s, trunk %s" %(str(access), str(trunk))) return (access, trunk) def datapath_leave_callback(dpid): logger.info('Switch %x has left the network' % dpid) if inst.st.has_key(dpid): del inst.st[dpid] def datapath_join_callback(dpid, stats): switch_stats[dpid] = stats logger.info('Switch %x has joined the network (%d switches currently connected)' % (dpid, len(switch_stats))) logger.info('stats for switch : %s' % str(stats)) # Keep the mapping between port number and port name if not port_name2no_map.has_key(dpid) : port_name2no_map[dpid] = {} port_no2name_map[dpid] = {} for p in stats['ports'] : port_name2no_map[dpid][p['name']] = int(p['port_no']) port_no2name_map[dpid][int(p['port_no'])] = p['name'] # -- # Packet entry method. # Drop LLDP packets (or we get confused) and attempt learning and # forwarding # -- def packet_in_callback(dpid, inport, reason, len, bufid, packet): if not packet.parsed: log.msg('Ignoring incomplete packet',system='vlanswitch') if not inst.st.has_key(dpid): log.msg('registering new switch %x' % dpid,system='vlanswitch') inst.st[dpid] = {} # don't forward lldp packets if packet.type == ethernet.VLAN_TYPE : vlan_hdr =packet.find('vlan') logger.debug('vlan hdr %s' % str(vlan_hdr)) #if vlan_hdr.id == 1101 : # return CONTINUE pkt_type = vlan_hdr.eth_type else : logger.debug('packet is not tagged' ) pkt_type = packet.type if pkt_type == ethernet.LLDP_TYPE: return CONTINUE logger.debug('RCVD PKT %s' % str(packet)) inport_name = port_no2name_map[dpid][inport] logger.info('[%d:%s] %s -> %s ' % (dpid, inport_name, mac_to_str(packet.src), mac_to_str(packet.dst))) forward_packet(dpid, inport, packet, packet.arr, bufid) return CONTINUE class vlanswitch(Component): def __init__(self, ctxt): global inst, vlan_conf Component.__init__(self, ctxt) self.st = {} if self.check_vlan_conf(vlan_conf) == False : exit inst = self def install(self): inst.register_for_packet_in(packet_in_callback) inst.register_for_datapath_leave(datapath_leave_callback) inst.register_for_datapath_join(datapath_join_callback) def getInterface(self): return str(vlanswitch) def check_vlan_conf(self, v_conf) : return True def getFactory(): class Factory: def instance(self, ctxt): return vlanswitch(ctxt) return Factory()
_______________________________________________ nox-dev mailing list nox-dev@noxrepo.org http://noxrepo.org/mailman/listinfo/nox-dev