摘要:每個消息將通過一個的線程進行處理,并執行與所有模塊的消息相關聯的所有邏輯其他模塊也可以注冊類似交換機連接或斷開和端口狀態通知特定時間。默認情況下,使用地址和來識別設備。設備管理器將了解其他屬性,如地址。在消息轉發實現前,模塊將啟動。
FloodlightProvider
處理交換機之間的連接并將 OpenFlow 的消息轉化成其他模塊可以監聽的時間
決定某些特定的 OpenFLow 消息(即 PacketIn,FlowRemove,PortStatus 等)被分派到該偵聽消息的模塊的順序,模塊可以決定允許該消息進入下一個監聽對象或者停止處理消息
FloodlightProvider 如何工作?FloodlightProvider 使用 Netty 庫來處理到交換機的線程和連接。
每個 OpenFlow 消息將通過一個 Netty 的線程進行處理,并執行與所有模塊的消息相關聯的所有邏輯
其他模塊也可以注冊類似交換機連接或斷開和端口狀態通知特定時間。
為了使模塊注冊為基于 OpenFlow 消息的,必須實現 IOFMessageListener 接口
要監聽 OpenFlow 消息,要先向 FloodlightProvider 注冊
調用 IFloodlightProviderService(具體由 Controller 類實現)的 addOFMessageListener 方法進行注冊訂閱
核心工作是在 ListenerDispatcher 類來完成。
每次增加觀察者都會判斷是否是終結點(也就是不被其他的 Listener 所依賴),因為最終確定這些觀察者順序的時候就是由這些終結點開始往前進行 DFS 遍歷得到
@Override public synchronized void addOFMessageListener(OFType type, IOFMessageListener listener) { //先判斷與type對應的 ListenerDispatcher對象是否存在 ListenerDispatcherListenerDispatcher 維護這些觀察者,有依賴關系ldd = messageListeners.get(type); if (ldd == null) { ldd = new ListenerDispatcher (); messageListeners.put(type, ldd); } //注冊監聽type這個消息; ldd.addListener(type, listener); }
volatile Listlisteners = new ArrayList ();
//每個OF msg都有唯一的ListenerDispatcher對象,觀察者存在listeners鏈表中
private boolean ispre(U type, T l1, T l2) { return (l2.isCallbackOrderingPrereq(type, l1.getName()) || l1.isCallbackOrderingPostreq(type, l2.getName())); }
返回兩個傳入的監聽器的順序
public void addListener(U type, T listener) { Listnewlisteners = new ArrayList (); if (listeners != null) newlisteners.addAll(listeners); newlisteners.add(listener); // Find nodes without outgoing edges // 查找沒有出邊的節點 List terminals = new ArrayList (); for (T i : newlisteners) { boolean isterm = true; for (T j : newlisteners) { if (ispre(type, i, j)) { //兩個都不關心前后順序的時候 isterm = false; break; } } if (isterm) { //關乎有前后順序的監聽模塊存入 terminals.add(i); } } if (terminals.size() == 0) { logger.error("No listener dependency solution: " + "No listeners without incoming dependencies"); listeners = newlisteners; return; } // visit depth-first traversing in the opposite order from // the dependencies. Note we will not generally detect cycles /** * 以相反順序訪問深度優先遍歷依賴。 注意我們通常不會檢測周期 */ HashSet visited = new HashSet (); List ordering = new ArrayList (); for (T term : terminals) { //進行排序 visit(newlisteners, type, visited, ordering, term); } listeners = ordering; }
private void visit(List監聽器具有的方法newlisteners, U type, HashSet visited, List ordering, T listener) { if (!visited.contains(listener)) { visited.add(listener); for (T i : newlisteners) { if (ispre(type, i, listener)) { visit(newlisteners, type, visited, ordering, i); } } ordering.add(listener); // } }
public interface IListener查看繼承了 IOFMessageListener 的Type Hierarchypublic enum Command { CONTINUE, STOP } 狀態值,用來判斷是否繼續執行 public String getName(); //用來判斷 name 的這個模塊是否要在當前對象之前執行 public boolean isCallbackOrderingPrereq(T type, String name); //用來判斷 name 的這個模塊是否要在當前對象之后執行 public boolean isCallbackOrderingPostreq(T type, String name); IOFMessageListener接口繼承了 IListener 接口,同時定義了 receive 方法 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx); 返回 CONTINUE 或者 STOP,繼續看每個繼承這個接口的模塊的重寫
TopologyManager 模塊的IOFMessageListener 重寫的方法:
@Override public String getName() { return MODULE_NAME; //此處為 topology,每個模塊都有自己的 MODULE_NAME } @Override public boolean isCallbackOrderingPrereq(OFType type, String name) { //從此處可以看出,在執行這個模塊之前,需要先執行 MODULE_NAME 為 linkiscovery 的模塊 return "linkdiscovery".equals(name); } @Override public boolean isCallbackOrderingPostreq(OFType type, String name) { return false; } @Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: ctrIncoming.increment();//計數器,加一 //調用這里的執行方法 return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; }
通過 Type Hierarchy 可以找到Packet-In消息處理順序的幾個模塊
FloodlightContextStore 代表的是一種緩存模型(利用的是ConcurrentHashMap,線程安全的 HashMap)
里面存儲的是上下文相關的對象,能夠根據相應的key得到具體的 Object
存在的意義是Floodlight中注冊監聽某個事件的listener可以在被調用的時候直接從中取出上下文信息(context information)
基本數據結構,這是一個上下文對象,Floodlight代碼監聽器可以注冊它,稍后可以檢索與事件相關聯的上下文信息
public class FloodlightContext { protected ConcurrentHashMapstorage = new ConcurrentHashMap (); public ConcurrentHashMap getStorage() { return storage; } }
創建了一個 HashMap storage,
public class FloodlightContextStore{ @SuppressWarnings("unchecked") public V get(FloodlightContext bc, String key) { return (V)bc.storage.get(key); } public void put(FloodlightContext bc, String key, V value) { bc.storage.put(key, value); } public void remove(FloodlightContext bc, String key) { bc.storage.remove(key); } }
一個FloodlightContextStore對象,可用于PACKET-IN有效內容,消息對象是Ethernet類型
public static final FloodlightContextStoreLinkDiscoveryManager 模塊bcStore = new FloodlightContextStore ();
鏈接發現服務負責發現和維護 OpenFlow 網絡中的網絡連接的狀態
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: ctrIncoming.increment(); return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; }
主要使用了 handlePacketIn()方法
protected Command handlePacketIn(DatapathId sw, OFPacketIn pi, FloodlightContext cntx) { //提取 Packet-In 的有效分組內容 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); if (eth.getPayload() instanceof BSN) { BSN bsn = (BSN) eth.getPayload(); if (bsn == null) return Command.STOP; if (bsn.getPayload() == null) return Command.STOP; // It could be a packet other than BSN LLDP, therefore // continue with the regular processing. // 它可以是除BSN LLDP之外的分組,因此繼續進行常規處理。 if (bsn.getPayload() instanceof LLDP == false) return Command.CONTINUE; return handleLldp((LLDP) bsn.getPayload(), sw, inPort, false, cntx); } else if (eth.getPayload() instanceof LLDP) { return handleLldp((LLDP) eth.getPayload(), sw, inPort, true, cntx); } else if (eth.getEtherType().getValue() < 1536 && eth.getEtherType().getValue() >= 17) { long destMac = eth.getDestinationMACAddress().getLong(); if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) { ctrLinkLocalDrops.increment(); if (log.isTraceEnabled()) { log.trace("Ignoring packet addressed to 802.1D/Q " + "reserved address."); } return Command.STOP; } } else if (eth.getEtherType().getValue() < 17) { log.error("Received invalid ethertype of {}.", eth.getEtherType()); return Command.STOP; } if (ignorePacketInFromSource(eth.getSourceMACAddress())) { ctrIgnoreSrcMacDrops.increment(); return Command.STOP; } // If packet-in is from a quarantine port, stop processing. NodePortTuple npt = new NodePortTuple(sw, inPort); if (quarantineQueue.contains(npt)) { ctrQuarantineDrops.increment(); return Command.STOP; } return Command.CONTINUE; }TopolopyManager
為控制器維護拓撲信息,以及在網絡中尋找路由
IOFMessageListener 的 receive 方法 @Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: ctrIncoming.increment(); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; } 主要使用了processPacketInMessage()方法 protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { // get the packet-in switch. Ethernet eth = IFloodlightProviderService.bcStore. get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); if (eth.getPayload() instanceof BSN) { BSN bsn = (BSN) eth.getPayload(); if (bsn == null) return Command.STOP; if (bsn.getPayload() == null) return Command.STOP; // 可能不是 BSN LLDP,繼續常規處理 if (bsn.getPayload() instanceof LLDP == false) return Command.CONTINUE; doFloodBDDP(sw.getId(), pi, cntx); return Command.STOP; } else { return dropFilter(sw.getId(), pi, cntx); } }DeviceManagerImpl
DeviceManager基于在網絡中看到的MAC地址創建設備
它跟蹤映射到設備的任何網絡地址及其在網絡中的位置
設備管理器通過 PACKET-IN 消息請求了解設備,通過 PACKET-IN 消息獲取信息,根據實體如何建立進行分類。默認情況下,entity classifies 使用 MAC 地址和 VLAN 來識別設備。這兩個屬性定義一個獨一無二的設備。設備管理器將了解其他屬性,如 IP 地址。
信息中的一個重要的部分是設備的連接點,如果一個交換機接受到一個 PACKET-IN 消息,則交換機將會創建一個連接點,設備也會根據時間清空連接點,IP 地址,以及設備本身,最近看到的時間戳是用來保持清空過程的控制
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: cntIncoming.increment(); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; }
主要使用了processPacketInMessage()方法
protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); // Extract source entity information Entity srcEntity = getSourceEntityFromPacket(eth, sw.getId(), inPort); if (srcEntity == null) { cntInvalidSource.increment(); return Command.STOP; } // Learn from ARP packet for special VRRP settings. // In VRRP settings, the source MAC address and sender MAC // addresses can be different. In such cases, we need to learn // the IP to MAC mapping of the VRRP IP address. The source // entity will not have that information. Hence, a separate call // to learn devices in such cases. learnDeviceFromArpResponseData(eth, sw.getId(), inPort); // Learn/lookup device information Device srcDevice = learnDeviceByEntity(srcEntity); if (srcDevice == null) { cntNoSource.increment(); return Command.STOP; } // Store the source device in the context fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice); // Find the device matching the destination from the entity // classes of the source. if (eth.getDestinationMACAddress().getLong() == 0) { cntInvalidDest.increment(); return Command.STOP; } Entity dstEntity = getDestEntityFromPacket(eth); Device dstDevice = null; if (dstEntity != null) { dstDevice = findDestByEntity(srcDevice.getEntityClass(), dstEntity); if (dstDevice != null) fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice); else cntNoDest.increment(); } else { cntNoDest.increment(); } if (logger.isTraceEnabled()) { logger.trace("Received PI: {} on switch {}, port {} *** eth={}" + " *** srcDev={} *** dstDev={} *** ", new Object[] { pi, sw.getId().toString(), inPort, eth, srcDevice, dstDevice }); } snoopDHCPClientName(eth, srcDevice); return Command.CONTINUE; }vitualNetworkFilter
虛擬網絡過濾器模塊是基于虛擬化網絡的數據鏈路層,它允許你在獨立的數據鏈路層上創建多個邏輯鏈路
若是使用 floodlightdefault.properties 則沒有這個模塊
如何工作在 Floodlight 啟動時,沒有虛擬網絡創建,這時主機之間不能相互通信。
一旦用戶創建虛擬網絡,則主機就能夠被添加。
在 PACKET-IN 消息轉發實現前,模塊將啟動。
一旦,一條 PACKET-IN 消息被接受,模塊將查看源 MAC 地址和目的 MAC 地址,如果2個 MAC 地址是同一個虛擬網絡,模塊將返回 Command.CONINUE消息,并且繼續處理流。如果MAC 地址不在同一個虛擬網絡則返回 Command.STOP 消息,并丟棄包
必須在同一個物理數據鏈路層中
每個虛擬網絡只能擁有一個網關()【一個網關可被多個虛擬網絡共享】
多播和廣播沒有被隔離
允許所有的 DHCP 路徑
配置該模塊可用于 OpenStack 的部署
包含此模塊的默認配置文件位置:
src/main/resources/neutron.properties
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: return processPacketIn(sw, (OFPacketIn)msg, cntx); default: break; } log.warn("Received unexpected message {}", msg); return Command.CONTINUE; }
主要使用了processPacketIn()方法
protected Command processPacketIn(IOFSwitch sw, OFPacketIn msg, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); Command ret = Command.STOP; String srcNetwork = macToGuid.get(eth.getSourceMACAddress()); // If the host is on an unknown network we deny it. // We make exceptions for ARP and DHCP. if (eth.isBroadcast() || eth.isMulticast() || isDefaultGateway(eth) || isDhcpPacket(eth)) { ret = Command.CONTINUE; } else if (srcNetwork == null) { log.trace("Blocking traffic from host {} because it is not attached to any network.", eth.getSourceMACAddress().toString()); ret = Command.STOP; } else if (oneSameNetwork(eth.getSourceMACAddress(), eth.getDestinationMACAddress())) { // if they are on the same network continue ret = Command.CONTINUE; } if (log.isTraceEnabled()) log.trace("Results for flow between {} and {} is {}", new Object[] {eth.getSourceMACAddress(), eth.getDestinationMACAddress(), ret}); /* * TODO - figure out how to still detect gateways while using * drop mods if (ret == Command.STOP) { if (!(eth.getPayload() instanceof ARP)) doDropFlow(sw, msg, cntx); } */ return ret; }LoadBalancer
IOFMessageListener 的 receive 方法
@Override public net.floodlightcontroller.core.IListener.Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: return processPacketIn(sw, (OFPacketIn)msg, cntx); default: break; } log.warn("Received unexpected message {}", msg); return Command.CONTINUE; }
主要使用了processPacketIn()方法
private net.floodlightcontroller.core.IListener.Command processPacketIn(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); IPacket pkt = eth.getPayload(); if (eth.isBroadcast() || eth.isMulticast()) { // handle ARP for VIP if (pkt instanceof ARP) { // retrieve arp to determine target IP address ARP arpRequest = (ARP) eth.getPayload(); IPv4Address targetProtocolAddress = arpRequest.getTargetProtocolAddress(); if (vipIpToId.containsKey(targetProtocolAddress.getInt())) { String vipId = vipIpToId.get(targetProtocolAddress.getInt()); vipProxyArpReply(sw, pi, cntx, vipId); return Command.STOP; } } } else { // currently only load balance IPv4 packets - no-op for other traffic if (pkt instanceof IPv4) { IPv4 ip_pkt = (IPv4) pkt; // If match Vip and port, check pool and choose member int destIpAddress = ip_pkt.getDestinationAddress().getInt(); if (vipIpToId.containsKey(destIpAddress)){ IPClient client = new IPClient(); client.ipAddress = ip_pkt.getSourceAddress(); client.nw_proto = ip_pkt.getProtocol(); if (ip_pkt.getPayload() instanceof TCP) { TCP tcp_pkt = (TCP) ip_pkt.getPayload(); client.srcPort = tcp_pkt.getSourcePort(); client.targetPort = tcp_pkt.getDestinationPort(); } if (ip_pkt.getPayload() instanceof UDP) { UDP udp_pkt = (UDP) ip_pkt.getPayload(); client.srcPort = udp_pkt.getSourcePort(); client.targetPort = udp_pkt.getDestinationPort(); } if (ip_pkt.getPayload() instanceof ICMP) { client.srcPort = TransportPort.of(8); client.targetPort = TransportPort.of(0); } LBVip vip = vips.get(vipIpToId.get(destIpAddress)); if (vip == null) // fix dereference violations return Command.CONTINUE; LBPool pool = pools.get(vip.pickPool(client)); if (pool == null) // fix dereference violations return Command.CONTINUE; LBMember member = members.get(pool.pickMember(client)); if(member == null) //fix dereference violations return Command.CONTINUE; // for chosen member, check device manager and find and push routes, in both directions pushBidirectionalVipRoutes(sw, pi, cntx, client, member); // packet out based on table rule pushPacket(pkt, sw, pi.getBufferId(), (pi.getVersion().compareTo(OFVersion.OF_12) < 0) ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT), OFPort.TABLE, cntx, true); return Command.STOP; } } } // bypass non-load-balanced traffic for normal processing (forwarding) return Command.CONTINUE; }ForwardingBase
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: IRoutingDecision decision = null; if (cntx != null) { decision = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION); } return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx); default: break; } return Command.CONTINUE; }
主要使用了processPacketInMessage()方法
public abstract Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx);
所有繼承了 ForwardingBase 的子類Forwarding重寫了這個方法,實現具體的操作
@Override public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); // We found a routing decision (i.e. Firewall is enabled... it"s the only thing that makes RoutingDecisions) if (decision != null) { if (log.isTraceEnabled()) { log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi); } switch(decision.getRoutingAction()) { case NONE: // don"t do anything return Command.CONTINUE; case FORWARD_OR_FLOOD: case FORWARD: doForwardFlow(sw, pi, decision, cntx, false); return Command.CONTINUE; case MULTICAST: // treat as broadcast doFlood(sw, pi, decision, cntx); return Command.CONTINUE; case DROP: doDropFlow(sw, pi, decision, cntx); return Command.CONTINUE; default: log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction()); return Command.CONTINUE; } } else { // No routing decision was found. Forward to destination or flood if bcast or mcast. if (log.isTraceEnabled()) { log.trace("No decision was made for PacketIn={}, forwarding", pi); } if (eth.isBroadcast() || eth.isMulticast()) { doFlood(sw, pi, decision, cntx); } else { doForwardFlow(sw, pi, decision, cntx, false); } } return Command.CONTINUE; }PACKET-IN
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76540.html
摘要:每個具體的模塊都會重寫這幾個函數,下面舉個的例子。獲得的服務返回服務實現類和實現用的對象的。服務是指繼承了接口的類。模塊使用方法可以獲得對應的服務列表,可以到源碼去看對應的服務功能。 Floodlight 的 Main 解析圖 showImg(https://segmentfault.com/img/remote/1460000015816841?w=2048&h=2341); 需要...
摘要:在學習源碼的過程中,給我幫助最大的就是這個系列文章,于是決定基于這個系列文章談一下自己的理解。到此為止,首次渲染就完成啦總結從啟動到元素渲染到頁面,并不像看起來這么簡單,中間經歷了復雜的層級調用。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個非常艱辛的過...
摘要:依賴注入和控制反轉,這兩個詞經常一起出現。一句話表述他們之間的關系依賴注入是控制反轉的一種實現方式。而兩者有大量的代碼都是可以共享的,這就是依賴注入的使用場景了。下一步就是創建具體的依賴內容,然后注入到需要的地方這里的等于這個對象。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級...
前言 本文所有內容全部發布再個人博客主頁 https://github.com/muwoo/blogs歡迎訂閱。不過最近因為事情比較多,有一段時間沒有更新了,后面打算繼續不斷學習更新,歡迎小伙伴一起溝通交流~ 最近更新 前端單測的那些事 基于virtual dom 的canvas渲染 js Event loop 機制簡介 axios 核心源碼實現原理 JS 數據類型、賦值、深拷貝和淺拷貝 j...
前言 本文所有內容全部發布再個人博客主頁 https://github.com/muwoo/blogs歡迎訂閱。不過最近因為事情比較多,有一段時間沒有更新了,后面打算繼續不斷學習更新,歡迎小伙伴一起溝通交流~ 最近更新 前端單測的那些事 基于virtual dom 的canvas渲染 js Event loop 機制簡介 axios 核心源碼實現原理 JS 數據類型、賦值、深拷貝和淺拷貝 j...
閱讀 1655·2021-09-26 09:55
閱讀 5248·2021-09-22 15:40
閱讀 2013·2019-08-30 15:53
閱讀 1497·2019-08-30 11:15
閱讀 1715·2019-08-29 15:41
閱讀 1869·2019-08-28 18:13
閱讀 3146·2019-08-26 12:00
閱讀 1668·2019-08-26 10:30