172b562a76475ff22c18dc619ee92bad1afdeda9
[affinity.git] / l2agent / src / main / java / org / opendaylight / l2agent / L2Agent.java
1 /*
2  * Copyright (c) 2013 Plexxi, Inc. and others.  All rights reserved.
3  */
4
5 /*
6  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
7  *
8  * This program and the accompanying materials are made available under the
9  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
10  * and is available at http://www.eclipse.org/legal/epl-v10.html
11  */
12
13 /*
14  * Adapted from tutorial L2 forwarding demo (http://archive.openflow.org/).
15  */
16 package org.opendaylight.affinity.l2agent;
17
18 import java.net.InetAddress;
19 import java.net.UnknownHostException;
20 import java.util.List;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.lang.String;
26 import java.util.Map;
27 import java.util.HashMap;
28 import java.util.Timer;
29 import java.util.TimerTask;
30 import java.util.concurrent.ConcurrentHashMap;
31
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import org.opendaylight.controller.sal.action.Action;
36 import org.opendaylight.controller.sal.action.Output;
37 import org.opendaylight.controller.sal.action.Flood;
38 import org.opendaylight.controller.sal.core.ConstructionException;
39 import org.opendaylight.controller.sal.core.Node;
40 import org.opendaylight.controller.sal.core.NodeConnector;
41 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
42 import org.opendaylight.controller.sal.flowprogrammer.Flow;
43 import org.opendaylight.controller.sal.match.Match;
44 import org.opendaylight.controller.sal.match.MatchType;
45 import org.opendaylight.controller.sal.match.MatchField;
46 import org.opendaylight.controller.sal.packet.ARP;
47 import org.opendaylight.controller.sal.packet.BitBufferHelper;
48 import org.opendaylight.controller.sal.packet.Ethernet;
49 import org.opendaylight.controller.sal.packet.IDataPacketService;
50 import org.opendaylight.controller.sal.packet.IListenDataPacket;
51 import org.opendaylight.controller.sal.packet.Packet;
52 import org.opendaylight.controller.sal.packet.PacketResult;
53 import org.opendaylight.controller.sal.packet.RawPacket;
54 import org.opendaylight.controller.sal.utils.EtherTypes;
55 import org.opendaylight.controller.sal.utils.Status;
56 import org.opendaylight.controller.sal.utils.NetUtils;
57 import org.opendaylight.controller.switchmanager.ISwitchManager;
58 import org.opendaylight.controller.switchmanager.Subnet;
59
60 public class L2Agent implements IListenDataPacket {
61     private static final Logger logger = LoggerFactory
62             .getLogger(L2Agent.class);
63     private ISwitchManager switchManager = null;
64     private IFlowProgrammerService programmer = null;
65     private IDataPacketService dataPacketService = null;
66     private Map<Node, Map<Long, NodeConnector>> mac_to_ports = new HashMap<Node, Map<Long, NodeConnector>>();
67     private String function = "switch";
68
69     void setDataPacketService(IDataPacketService s) {
70         this.dataPacketService = s;
71     }
72
73     void unsetDataPacketService(IDataPacketService s) {
74         if (this.dataPacketService == s) {
75             this.dataPacketService = null;
76         }
77     }
78
79     public void setFlowProgrammerService(IFlowProgrammerService s)
80     {
81         this.programmer = s;
82     }
83
84     public void unsetFlowProgrammerService(IFlowProgrammerService s) {
85         if (this.programmer == s) {
86             this.programmer = null;
87         }
88     }
89
90     void setSwitchManager(ISwitchManager s) {
91         logger.debug("SwitchManager set");
92         this.switchManager = s;
93     }
94
95     void unsetSwitchManager(ISwitchManager s) {
96         if (this.switchManager == s) {
97             logger.debug("SwitchManager removed!");
98             this.switchManager = null;
99         }
100     }
101
102     /**
103      * Function called by the dependency manager when all the required
104      * dependencies are satisfied
105      *
106      */
107     void init() {
108         logger.info("Initialized");
109     }
110
111     /**
112      * Function called by the dependency manager when at least one
113      * dependency become unsatisfied or when the component is shutting
114      * down because for example bundle is being stopped.
115      *
116      */
117     void destroy() {
118     }
119
120     /**
121      * Function called by dependency manager after "init ()" is called
122      * and after the services provided by the class are registered in
123      * the service registry
124      *
125      */
126     void start() {
127         logger.info("Started");
128     }
129
130     /**
131      * Function called by the dependency manager before the services
132      * exported by the component are unregistered, this will be
133      * followed by a "destroy ()" calls
134      *
135      */
136     void stop() {
137         logger.info("Stopped");
138     }
139
140     private void floodPacket(RawPacket inPkt) {
141         NodeConnector incoming_connector = inPkt.getIncomingNodeConnector();
142         Node incoming_node = incoming_connector.getNode();
143
144         Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
145         if (formattedPak instanceof Ethernet) {
146             byte[] srcMAC = ((Ethernet)formattedPak).getSourceMACAddress();
147             byte[] dstMAC = ((Ethernet)formattedPak).getDestinationMACAddress();
148
149             long srcMAC_val = BitBufferHelper.toNumber(srcMAC);
150             long dstMAC_val = BitBufferHelper.toNumber(dstMAC);
151         }
152
153         Set<NodeConnector> nodeConnectors =
154                 this.switchManager.getUpNodeConnectors(incoming_node);
155
156         for (NodeConnector p : nodeConnectors) {
157             if (!p.equals(incoming_connector)) {
158                 try {
159                     RawPacket destPkt = new RawPacket(inPkt);
160                     destPkt.setOutgoingNodeConnector(p);
161                     this.dataPacketService.transmitDataPacket(destPkt);
162                 } catch (ConstructionException e2) {
163                     continue;
164                 }
165             }
166         }
167     }
168
169     @Override
170     public PacketResult receiveDataPacket(RawPacket inPkt) {
171         if (inPkt == null) {
172             return PacketResult.IGNORED;
173         }
174         logger.trace("Received a frame of size: {}",
175                         inPkt.getPacketData().length);
176
177         Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
178         NodeConnector incoming_connector = inPkt.getIncomingNodeConnector();
179         Node incoming_node = incoming_connector.getNode();
180
181         if (formattedPak instanceof Ethernet) {
182             byte[] srcMAC = ((Ethernet)formattedPak).getSourceMACAddress();
183             byte[] dstMAC = ((Ethernet)formattedPak).getDestinationMACAddress();
184
185             // Hub implementation
186             if (function.equals("hub")) {
187                 floodPacket(inPkt);
188                 return PacketResult.CONSUME;
189             }
190
191             // Switch
192             else {
193                 long srcMAC_val = BitBufferHelper.toNumber(srcMAC);
194                 long dstMAC_val = BitBufferHelper.toNumber(dstMAC);
195
196                 Match match = new Match();
197                 match.setField( new MatchField(MatchType.IN_PORT, incoming_connector) );
198                 match.setField( new MatchField(MatchType.DL_DST, dstMAC.clone()) );
199
200                 // Set up the mapping: switch -> src MAC address -> incoming port
201                 if (this.mac_to_ports.get(incoming_node) == null) {
202                     this.mac_to_ports.put(incoming_node, new HashMap<Long, NodeConnector>());
203                 }
204
205                 // Only replace if we don't know the mapping.  This
206                 // saves us from over-writing correct mappings with
207                 // incorrect ones we get during flooding.
208                 //
209                 // TODO: this should never happen..
210                 if (this.mac_to_ports.get(incoming_node).get(srcMAC_val) == null) {
211                     this.mac_to_ports.get(incoming_node).put(srcMAC_val, incoming_connector);
212                 }
213
214                 NodeConnector dst_connector = this.mac_to_ports.get(incoming_node).get(dstMAC_val);
215
216                 // Do I know the destination MAC?
217                 if (dst_connector != null) {
218
219                     List<Action> actions = new ArrayList<Action>();
220                     actions.add(new Output(dst_connector));
221
222                     Flow f = new Flow(match, actions);
223
224                     // Modify the flow on the network node
225                     Status status = programmer.addFlow(incoming_node, f);
226                     if (!status.isSuccess()) {
227                         logger.warn(
228                                 "SDN Plugin failed to program the flow: {}. The failure is: {}",
229                                 f, status.getDescription());
230                         return PacketResult.IGNORED;
231                     }
232                     logger.info("Installed flow {} in node {}", f, incoming_node);
233
234                     // TODO: Testing.  What do the flows on this node look like now?
235                     //                    new FlowStatisticsConverter(flows).getFlowOnNodeList(node)
236                 }
237                 else {
238                     floodPacket(inPkt);
239                 }
240             }
241         }
242         return PacketResult.IGNORED;
243     }
244     
245     public NodeConnector lookup(Node node, byte [] dstMAC) {
246         return this.mac_to_ports.get(node).get(dstMAC);
247     }
248 }