blob: b25f615253df9f0ee0732d55c2332b0150a8d339 [file] [log] [blame]
Amit Ghosh47243cb2017-07-26 05:08:53 +01001/*
Deepa vaddireddy0060f532017-08-04 06:46:05 +00002 * Copyright 2017-present Open Networking Foundation
Amit Ghosh47243cb2017-07-26 05:08:53 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.opencord.dhcpl2relay;
17
18import com.google.common.collect.ImmutableSet;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000019import com.google.common.collect.Lists;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053020import com.google.common.collect.Maps;
Amit Ghosh8951f042017-08-10 13:48:10 +010021import com.google.common.collect.Sets;
Amit Ghosh47243cb2017-07-26 05:08:53 +010022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
Amit Ghosh47243cb2017-07-26 05:08:53 +010029import org.onlab.packet.DHCP;
30import org.onlab.packet.DHCPOption;
31import org.onlab.packet.DHCPPacketType;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000032import org.onlab.packet.Ethernet;
Amit Ghosh47243cb2017-07-26 05:08:53 +010033import org.onlab.packet.IPv4;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053034import org.onlab.packet.IpAddress;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000035import org.onlab.packet.MacAddress;
Amit Ghosh47243cb2017-07-26 05:08:53 +010036import org.onlab.packet.TpPort;
37import org.onlab.packet.UDP;
38import org.onlab.packet.VlanId;
Amit Ghosh47243cb2017-07-26 05:08:53 +010039import org.onlab.util.Tools;
40import org.onosproject.cfg.ComponentConfigService;
41import org.onosproject.core.ApplicationId;
42import org.onosproject.core.CoreService;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053043import org.onosproject.mastership.MastershipEvent;
44import org.onosproject.mastership.MastershipListener;
45import org.onosproject.mastership.MastershipService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010046import org.onosproject.net.AnnotationKeys;
47import org.onosproject.net.ConnectPoint;
Amit Ghosh83c8c892017-11-09 11:08:27 +000048import org.onosproject.net.Device;
49import org.onosproject.net.DeviceId;
Amit Ghosh47243cb2017-07-26 05:08:53 +010050import org.onosproject.net.Host;
51import org.onosproject.net.Port;
Amit Ghosh83c8c892017-11-09 11:08:27 +000052import org.onosproject.net.PortNumber;
Amit Ghosh47243cb2017-07-26 05:08:53 +010053import org.onosproject.net.config.ConfigFactory;
54import org.onosproject.net.config.NetworkConfigEvent;
55import org.onosproject.net.config.NetworkConfigListener;
56import org.onosproject.net.config.NetworkConfigRegistry;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053057import org.onosproject.net.device.DeviceEvent;
58import org.onosproject.net.device.DeviceListener;
Amit Ghosh47243cb2017-07-26 05:08:53 +010059import org.onosproject.net.device.DeviceService;
60import org.onosproject.net.flow.DefaultTrafficSelector;
61import org.onosproject.net.flow.DefaultTrafficTreatment;
62import org.onosproject.net.flow.TrafficSelector;
63import org.onosproject.net.flow.TrafficTreatment;
64import org.onosproject.net.host.HostService;
65import org.onosproject.net.packet.DefaultOutboundPacket;
66import org.onosproject.net.packet.OutboundPacket;
67import org.onosproject.net.packet.PacketContext;
68import org.onosproject.net.packet.PacketPriority;
69import org.onosproject.net.packet.PacketProcessor;
70import org.onosproject.net.packet.PacketService;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053071import org.opencord.dhcpl2relay.packet.DhcpOption82;
Amit Ghosh47243cb2017-07-26 05:08:53 +010072import org.opencord.sadis.SubscriberAndDeviceInformation;
73import org.opencord.sadis.SubscriberAndDeviceInformationService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010074import org.osgi.service.component.ComponentContext;
75import org.slf4j.Logger;
76import org.slf4j.LoggerFactory;
77
78import java.nio.ByteBuffer;
Amit Ghosh2095dc62017-09-25 20:56:55 +010079import java.util.Arrays;
Amit Ghosh83c8c892017-11-09 11:08:27 +000080import java.util.ArrayList;
Amit Ghosh47243cb2017-07-26 05:08:53 +010081import java.util.Dictionary;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000082import java.util.List;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053083import java.util.Map;
Amit Ghosh83c8c892017-11-09 11:08:27 +000084import java.util.Optional;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000085import java.util.Set;
Amit Ghosh8951f042017-08-10 13:48:10 +010086import java.util.concurrent.atomic.AtomicReference;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000087import java.util.stream.Collectors;
Amit Ghosh47243cb2017-07-26 05:08:53 +010088
89import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
90import static org.onlab.packet.MacAddress.valueOf;
91import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
92
93/**
94 * DHCP Relay Agent Application Component.
95 */
96@Component(immediate = true)
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053097public class
98DhcpL2Relay {
Amit Ghosh47243cb2017-07-26 05:08:53 +010099
100 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
101 private final Logger log = LoggerFactory.getLogger(getClass());
102 private final InternalConfigListener cfgListener =
103 new InternalConfigListener();
104
105 private final Set<ConfigFactory> factories = ImmutableSet.of(
106 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
107 DhcpL2RelayConfig.class,
108 "dhcpl2relay") {
109 @Override
110 public DhcpL2RelayConfig createConfig() {
111 return new DhcpL2RelayConfig();
112 }
113 }
114 );
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected NetworkConfigRegistry cfgService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected CoreService coreService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected PacketService packetService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected HostService hostService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected ComponentConfigService componentConfigService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected SubscriberAndDeviceInformationService subsService;
133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
135 protected DeviceService deviceService;
136
Amit Ghosh8951f042017-08-10 13:48:10 +0100137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected MastershipService mastershipService;
139
Amit Ghosh47243cb2017-07-26 05:08:53 +0100140 @Property(name = "option82", boolValue = true,
141 label = "Add option 82 to relayed packets")
142 protected boolean option82 = true;
143
Amit Ghosha17354e2017-08-23 12:56:04 +0100144 @Property(name = "enableDhcpBroadcastReplies", boolValue = false,
Amit Ghosh2095dc62017-09-25 20:56:55 +0100145 label = "Ask the DHCP Server to send back replies as L2 broadcast")
Amit Ghosha17354e2017-08-23 12:56:04 +0100146 protected boolean enableDhcpBroadcastReplies = false;
147
Amit Ghosh47243cb2017-07-26 05:08:53 +0100148 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
149 new DhcpRelayPacketProcessor();
150
Amit Ghosh8951f042017-08-10 13:48:10 +0100151 private InnerMastershipListener changeListener = new InnerMastershipListener();
152 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100153
Amit Ghosh8951f042017-08-10 13:48:10 +0100154 // connect points to the DHCP server
155 Set<ConnectPoint> dhcpConnectPoints;
156 private AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100157 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
158 private ApplicationId appId;
159
Amit Ghosha17354e2017-08-23 12:56:04 +0100160 static Map<String, DhcpAllocationInfo> allocationMap = Maps.newConcurrentMap();
Amit Ghosh83c8c892017-11-09 11:08:27 +0000161 private boolean modifyClientPktsSrcDstMac = false;
162 //Whether to use the uplink port of the OLTs to send/receive messages to the DHCP server
163 private boolean useOltUplink = false;
Amit Ghosha17354e2017-08-23 12:56:04 +0100164
Amit Ghosh47243cb2017-07-26 05:08:53 +0100165 @Activate
166 protected void activate(ComponentContext context) {
167 //start the dhcp relay agent
168 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
169 componentConfigService.registerProperties(getClass());
170
171 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100172 mastershipService.addListener(changeListener);
173 deviceService.addListener(deviceListener);
174
Amit Ghosh47243cb2017-07-26 05:08:53 +0100175 factories.forEach(cfgService::registerConfigFactory);
176 //update the dhcp server configuration.
177 updateConfig();
178 //add the packet services.
179 packetService.addProcessor(dhcpRelayPacketProcessor,
180 PacketProcessor.director(0));
181 requestDhcpPackets();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000182 if (context != null) {
183 modified(context);
184 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100185
186 log.info("DHCP-L2-RELAY Started");
187 }
188
189 @Deactivate
190 protected void deactivate() {
191 cfgService.removeListener(cfgListener);
192 factories.forEach(cfgService::unregisterConfigFactory);
193 packetService.removeProcessor(dhcpRelayPacketProcessor);
194 cancelDhcpPackets();
195
196 componentConfigService.unregisterProperties(getClass(), false);
Deepa Vaddireddy77a6ac72017-09-20 20:36:52 +0530197 deviceService.removeListener(deviceListener);
198 mastershipService.removeListener(changeListener);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100199 log.info("DHCP-L2-RELAY Stopped");
200 }
201
202 @Modified
203 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000204
Amit Ghosh47243cb2017-07-26 05:08:53 +0100205 Dictionary<?, ?> properties = context.getProperties();
206
207 Boolean o = Tools.isPropertyEnabled(properties, "option82");
208 if (o != null) {
209 option82 = o;
210 }
Amit Ghosh2095dc62017-09-25 20:56:55 +0100211
212 o = Tools.isPropertyEnabled(properties, "enableDhcpBroadcastReplies");
213 if (o != null) {
214 enableDhcpBroadcastReplies = o;
215 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100216 }
217
218 /**
219 * Checks if this app has been configured.
220 *
221 * @return true if all information we need have been initialized
222 */
223 private boolean configured() {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000224 if (!useOltUplink) {
225 return dhcpServerConnectPoint.get() != null;
226 }
227 return true;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100228 }
229
Amit Ghosh8951f042017-08-10 13:48:10 +0100230 /**
231 * Selects a connect point through an available device for which it is the master.
232 */
233 private void selectServerConnectPoint() {
234 synchronized (this) {
235 dhcpServerConnectPoint.set(null);
236 if (dhcpConnectPoints != null) {
237 // find a connect point through a device for which we are master
238 for (ConnectPoint cp: dhcpConnectPoints) {
239 if (mastershipService.isLocalMaster(cp.deviceId())) {
240 if (deviceService.isAvailable(cp.deviceId())) {
241 dhcpServerConnectPoint.set(cp);
242 }
243 log.info("DHCP connectPoint selected is {}", cp);
244 break;
245 }
246 }
247 }
248
249 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
250
251 if (dhcpServerConnectPoint.get() == null) {
252 log.error("Master of none, can't relay DHCP Message to server");
253 }
254 }
255 }
256
257 /**
258 * Updates the network configuration.
259 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100260 private void updateConfig() {
261 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
262 if (cfg == null) {
263 log.warn("Dhcp Server info not available");
264 return;
265 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100266
267 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
Amit Ghosh83c8c892017-11-09 11:08:27 +0000268 modifyClientPktsSrcDstMac = cfg.getModifySrcDstMacAddresses();
269 useOltUplink = cfg.getUseOltUplinkForServerPktInOut();
Amit Ghosh8951f042017-08-10 13:48:10 +0100270
Amit Ghosh83c8c892017-11-09 11:08:27 +0000271 if (!useOltUplink) {
272 selectServerConnectPoint();
273 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100274
Amit Ghosh47243cb2017-07-26 05:08:53 +0100275 log.info("dhcp server connect point: " + dhcpServerConnectPoint);
276 }
277
278 /**
Amit Ghosh83c8c892017-11-09 11:08:27 +0000279 * Returns all the uplink ports of OLTs configured in SADIS.
280 * Only ports visible in ONOS and for which this instance is master
281 * are returned
282 */
283 private List<ConnectPoint> getUplinkPortsOfOlts() {
284 List<ConnectPoint> cps = new ArrayList<>();
285
286 // find all the olt devices and if their uplink ports are visible
287 Iterable<Device> devices = deviceService.getDevices();
288 for (Device d : devices) {
289 // check if this device is provisioned in Sadis
290
291 log.debug("getUplinkPortsOfOlts: Checking mastership of {}", d);
292 // do only for devices for which we are the master
293 if (!mastershipService.isLocalMaster(d.id())) {
294 continue;
295 }
296
297 String devSerialNo = d.serialNumber();
298 SubscriberAndDeviceInformation deviceInfo = subsService.get(devSerialNo);
299 log.debug("getUplinkPortsOfOlts: Found device: {}", deviceInfo);
300 if (deviceInfo != null) {
301 // check if the uplink port with that number is available on the device
302 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
303 Port port = deviceService.getPort(d.id(), pNum);
304 log.debug("getUplinkPortsOfOlts: Found port: {}", port);
305 if (port != null) {
306 cps.add(new ConnectPoint(d.id(), pNum));
307 }
308 }
309 }
310 return cps;
311 }
312
313 /**
314 * Returns whether the passed port is the uplink port of the olt device.
315 */
316 private boolean isUplinkPortOfOlt(DeviceId dId, Port p) {
317 log.debug("isUplinkPortOfOlt: DeviceId: {} Port: {}", dId, p);
318 // do only for devices for which we are the master
319 if (!mastershipService.isLocalMaster(dId)) {
320 return false;
321 }
322
323 Device d = deviceService.getDevice(dId);
324 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
325
326 if (deviceInfo != null) {
327 return (deviceInfo.uplinkPort() == p.number().toLong());
328 }
329
330 return false;
331 }
332
333 /**
334 * Returns the connectPoint which is the uplink port of the OLT.
335 */
336 private ConnectPoint getUplinkConnectPointOfOlt(DeviceId dId) {
337
338 Device d = deviceService.getDevice(dId);
339 SubscriberAndDeviceInformation deviceInfo = subsService.get(d.serialNumber());
340 log.debug("getUplinkConnectPointOfOlt DeviceId: {} devInfo: {}", dId, deviceInfo);
341 if (deviceInfo != null) {
342 PortNumber pNum = PortNumber.portNumber(deviceInfo.uplinkPort());
343 Port port = deviceService.getPort(d.id(), pNum);
344 if (port != null) {
345 return new ConnectPoint(d.id(), pNum);
346 }
347 }
348
349 return null;
350 }
351
352 /**
353 * Request DHCP packet from particular connect point via PacketService.
354 */
355 private void requestDhcpPacketsFromConnectPoint(ConnectPoint cp) {
356 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
357 .matchEthType(Ethernet.TYPE_IPV4)
358 .matchInPort(cp.port())
359 .matchIPProtocol(IPv4.PROTOCOL_UDP)
360 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
361 packetService.requestPackets(selectorServer.build(),
362 PacketPriority.CONTROL, appId, Optional.of(cp.deviceId()));
363 }
364
365 /**
366 * Cancel DHCP packet from particular connect point via PacketService.
367 */
368 private void cancelDhcpPacketsFromConnectPoint(ConnectPoint cp) {
369 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
370 .matchEthType(Ethernet.TYPE_IPV4)
371 .matchInPort(cp.port())
372 .matchIPProtocol(IPv4.PROTOCOL_UDP)
373 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
374 packetService.cancelPackets(selectorServer.build(),
375 PacketPriority.CONTROL, appId, Optional.of(cp.deviceId()));
376 }
377
378 /**
Amit Ghosh47243cb2017-07-26 05:08:53 +0100379 * Request DHCP packet in via PacketService.
380 */
381 private void requestDhcpPackets() {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000382 if (!useOltUplink) {
383 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
384 .matchEthType(Ethernet.TYPE_IPV4)
385 .matchIPProtocol(IPv4.PROTOCOL_UDP)
386 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
387 packetService.requestPackets(selectorServer.build(),
388 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100389
Amit Ghosh83c8c892017-11-09 11:08:27 +0000390 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
391 .matchEthType(Ethernet.TYPE_IPV4)
392 .matchIPProtocol(IPv4.PROTOCOL_UDP)
393 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
394 packetService.requestPackets(selectorClient.build(),
395 PacketPriority.CONTROL, appId);
396 } else {
397 for (ConnectPoint cp: getUplinkPortsOfOlts()) {
398 log.debug("requestDhcpPackets: ConnectPoint: {}", cp);
399 requestDhcpPacketsFromConnectPoint(cp);
400 }
401 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100402
Amit Ghosh47243cb2017-07-26 05:08:53 +0100403 }
404
405 /**
406 * Cancel requested DHCP packets in via packet service.
407 */
408 private void cancelDhcpPackets() {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000409 if (!useOltUplink) {
410 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
411 .matchEthType(Ethernet.TYPE_IPV4)
412 .matchIPProtocol(IPv4.PROTOCOL_UDP)
413 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
414 packetService.cancelPackets(selectorServer.build(),
415 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100416
Amit Ghosh83c8c892017-11-09 11:08:27 +0000417 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
418 .matchEthType(Ethernet.TYPE_IPV4)
419 .matchIPProtocol(IPv4.PROTOCOL_UDP)
420 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
421 packetService.cancelPackets(selectorClient.build(),
422 PacketPriority.CONTROL, appId);
423 } else {
424 for (ConnectPoint cp: getUplinkPortsOfOlts()) {
425 cancelDhcpPacketsFromConnectPoint(cp);
426 }
427 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100428 }
429
Amit Ghosha17354e2017-08-23 12:56:04 +0100430 public static Map<String, DhcpAllocationInfo> allocationMap() {
431 return allocationMap;
432 }
433
Amit Ghosh47243cb2017-07-26 05:08:53 +0100434 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
435 String serialNo = deviceService.getDevice(context.inPacket().
436 receivedFrom().deviceId()).serialNumber();
437
438 return subsService.get(serialNo);
439 }
440
441 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
442 String serialNo = deviceService.getDevice(cp.deviceId()).
443 serialNumber();
444
445 return subsService.get(serialNo);
446 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100447
448 private MacAddress relayAgentMacAddress(PacketContext context) {
449
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000450 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100451 if (device == null) {
452 log.warn("Device not found for {}", context.inPacket().
453 receivedFrom());
454 return null;
455 }
456
457 return device.hardwareIdentifier();
458 }
459
460 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100461 return nasPortId(context.inPacket().receivedFrom());
462 }
463
464 private String nasPortId(ConnectPoint cp) {
465 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100466 return p.annotations().value(AnnotationKeys.PORT_NAME);
467 }
468
469 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100470 return subsService.get(nasPortId(context));
471 }
472
473 private VlanId cTag(PacketContext context) {
474 SubscriberAndDeviceInformation sub = getSubscriber(context);
475 if (sub == null) {
476 log.warn("Subscriber info not found for {}", context.inPacket().
477 receivedFrom());
478 return VlanId.NONE;
479 }
480 return sub.cTag();
481 }
482
Amit Ghosh8951f042017-08-10 13:48:10 +0100483 private VlanId cTag(ConnectPoint cp) {
484 String portId = nasPortId(cp);
485 SubscriberAndDeviceInformation sub = subsService.get(portId);
486 if (sub == null) {
487 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
488 return VlanId.NONE;
489 }
490 return sub.cTag();
491 }
492
493 private VlanId sTag(ConnectPoint cp) {
494 String portId = nasPortId(cp);
495 SubscriberAndDeviceInformation sub = subsService.get(portId);
496 if (sub == null) {
497 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
498 return VlanId.NONE;
499 }
500 return sub.sTag();
501 }
502
Amit Ghosh47243cb2017-07-26 05:08:53 +0100503 private VlanId sTag(PacketContext context) {
504 SubscriberAndDeviceInformation sub = getSubscriber(context);
505 if (sub == null) {
506 log.warn("Subscriber info not found for {}", context.inPacket().
507 receivedFrom());
508 return VlanId.NONE;
509 }
510 return sub.sTag();
511 }
512
513 private class DhcpRelayPacketProcessor implements PacketProcessor {
514
515 @Override
516 public void process(PacketContext context) {
517 if (!configured()) {
518 log.warn("Missing DHCP relay config. Abort packet processing");
519 return;
520 }
521
522 // process the packet and get the payload
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530523 Ethernet packet = context.inPacket().parsed();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000524
Amit Ghosh47243cb2017-07-26 05:08:53 +0100525 if (packet == null) {
526 log.warn("Packet is null");
527 return;
528 }
529
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000530 log.debug("Got a packet ", packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100531
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530532 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100533 IPv4 ipv4Packet = (IPv4) packet.getPayload();
534
535 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
536 UDP udpPacket = (UDP) ipv4Packet.getPayload();
537 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000538 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100539 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
540 //This packet is dhcp.
541 processDhcpPacket(context, packet, dhcpPayload);
542 }
543 }
544 }
545 }
546
547 //forward the packet to ConnectPoint where the DHCP server is attached.
Amit Ghosh83c8c892017-11-09 11:08:27 +0000548 private void forwardPacket(Ethernet packet, PacketContext context) {
549 ConnectPoint toSendTo = null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100550
Amit Ghosh83c8c892017-11-09 11:08:27 +0000551 if (!useOltUplink) {
552 toSendTo = dhcpServerConnectPoint.get();
553 } else {
554 toSendTo = getUplinkConnectPointOfOlt(context.inPacket().
555 receivedFrom().deviceId());
556 }
557
558 if (toSendTo != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100559 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh83c8c892017-11-09 11:08:27 +0000560 .setOutput(toSendTo.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100561 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh83c8c892017-11-09 11:08:27 +0000562 toSendTo.deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100563 ByteBuffer.wrap(packet.serialize()));
564 if (log.isTraceEnabled()) {
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530565 log.trace("Relaying packet to dhcp server {} at {}",
Amit Ghosh83c8c892017-11-09 11:08:27 +0000566 packet, toSendTo);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100567 }
568 packetService.emit(o);
569 } else {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000570 log.error("No connect point to send msg to DHCP Server");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100571 }
572 }
573
Amit Ghosha17354e2017-08-23 12:56:04 +0100574 // get the type of the DHCP packet
575 private DHCPPacketType getDhcpPacketType(DHCP dhcpPayload) {
576
577 for (DHCPOption option : dhcpPayload.getOptions()) {
578 if (option.getCode() == OptionCode_MessageType.getValue()) {
579 byte[] data = option.getData();
580 return DHCPPacketType.getType(data[0]);
581 }
582 }
583 return null;
584 }
585
Amit Ghosh47243cb2017-07-26 05:08:53 +0100586 //process the dhcp packet before sending to server
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530587 private void processDhcpPacket(PacketContext context, Ethernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100588 DHCP dhcpPayload) {
589 if (dhcpPayload == null) {
590 log.warn("DHCP payload is null");
591 return;
592 }
593
Amit Ghosha17354e2017-08-23 12:56:04 +0100594 DHCPPacketType incomingPacketType = getDhcpPacketType(dhcpPayload);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000595
Amit Ghosh47243cb2017-07-26 05:08:53 +0100596 log.info("Received DHCP Packet of type {}", incomingPacketType);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100597
598 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000599 case DHCPDISCOVER:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530600 Ethernet ethernetPacketDiscover =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000601 processDhcpPacketFromClient(context, packet);
602 if (ethernetPacketDiscover != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000603 forwardPacket(ethernetPacketDiscover, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000604 }
605 break;
606 case DHCPOFFER:
607 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530608 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000609 if (ethernetPacketOffer != null) {
610 sendReply(ethernetPacketOffer, dhcpPayload);
611 }
612 break;
613 case DHCPREQUEST:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530614 Ethernet ethernetPacketRequest =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000615 processDhcpPacketFromClient(context, packet);
616 if (ethernetPacketRequest != null) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000617 forwardPacket(ethernetPacketRequest, context);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000618 }
619 break;
620 case DHCPACK:
621 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530622 Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000623 if (ethernetPacketAck != null) {
624 sendReply(ethernetPacketAck, dhcpPayload);
625 }
626 break;
627 default:
628 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100629 }
630 }
631
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530632 private Ethernet processDhcpPacketFromClient(PacketContext context,
633 Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100634
635 MacAddress relayAgentMac = relayAgentMacAddress(context);
636 if (relayAgentMac == null) {
637 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100638 return null;
639 }
640
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530641 Ethernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100642
643 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
644 UDP udpPacket = (UDP) ipv4Packet.getPayload();
645 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
646
Amit Ghosha17354e2017-08-23 12:56:04 +0100647 if (enableDhcpBroadcastReplies) {
648 // We want the reply to come back as a L2 broadcast
649 dhcpPacket.setFlags((short) 0x8000);
650 }
651
652 // remove from the allocation map (used for display) as it's is
653 // requesting a fresh allocation
654 if (getDhcpPacketType(dhcpPacket) == DHCPPacketType.DHCPREQUEST) {
655
656 String portId = nasPortId(context.inPacket().receivedFrom());
657 SubscriberAndDeviceInformation sub = subsService.get(portId);
658 if (sub != null) {
659 allocationMap.remove(sub.nasPortId());
660 }
661 } // end allocation for display
662
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000663 if (option82) {
664 SubscriberAndDeviceInformation entry = getSubscriber(context);
665 if (entry == null) {
666 log.info("Dropping packet as subscriber entry is not available");
667 return null;
668 }
669
670 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
671 udpPacket.setPayload(dhcpPacketWithOption82);
672 }
673
674 ipv4Packet.setPayload(udpPacket);
675 etherReply.setPayload(ipv4Packet);
Amit Ghosh83c8c892017-11-09 11:08:27 +0000676 if (modifyClientPktsSrcDstMac) {
677 etherReply.setSourceMACAddress(relayAgentMac);
678 etherReply.setDestinationMACAddress(dhcpConnectMac);
679 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100680
Amit Ghosh8951f042017-08-10 13:48:10 +0100681 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100682 etherReply.setVlanID(cTag(context).toShort());
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530683 etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
684 etherReply.setQinQVID(sTag(context).toShort());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100685
Amit Ghosh8951f042017-08-10 13:48:10 +0100686 log.info("Finished processing packet -- sending packet {}", etherReply);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100687 return etherReply;
688 }
689
690 //build the DHCP offer/ack with proper client port.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530691 private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100692 // get dhcp header.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530693 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100694 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
695 UDP udpPacket = (UDP) ipv4Packet.getPayload();
696 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
697
Amit Ghosh47243cb2017-07-26 05:08:53 +0100698 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100699 ConnectPoint subsCp = getConnectPointOfClient(dstMac);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100700 // If we can't find the subscriber, can't process further
701 if (subsCp == null) {
702 return null;
703 }
Amit Ghosha17354e2017-08-23 12:56:04 +0100704 // if it's an ACK packet store the information for display purpose
705 if (getDhcpPacketType(dhcpPayload) == DHCPPacketType.DHCPACK) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100706
Amit Ghosha17354e2017-08-23 12:56:04 +0100707 String portId = nasPortId(subsCp);
708 SubscriberAndDeviceInformation sub = subsService.get(portId);
709 if (sub != null) {
710 List<DHCPOption> options = dhcpPayload.getOptions();
711 List<DHCPOption> circuitIds = options.stream()
712 .filter(option -> option.getCode() == DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
713 .collect(Collectors.toList());
714
715 String circuitId = "None";
716 if (circuitIds.size() == 1) {
Amit Ghosh2095dc62017-09-25 20:56:55 +0100717 byte[] array = circuitIds.get(0).getData();
718
719 try {
720 // we leave the first two bytes as they are the id and length
721 circuitId = new String(Arrays.copyOfRange(array, 2, array.length), "UTF-8");
722 } catch (Exception e) { }
Amit Ghosha17354e2017-08-23 12:56:04 +0100723 }
724
725 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
726
727 //storeDHCPAllocationInfo
728 DhcpAllocationInfo info = new DhcpAllocationInfo(circuitId, dstMac, ip);
729
730 allocationMap.put(sub.nasPortId(), info);
731 }
732 } // end storing of info
Amit Ghosh47243cb2017-07-26 05:08:53 +0100733
734 // we leave the srcMac from the original packet
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530735 etherReply.setDestinationMACAddress(dstMac);
736 etherReply.setQinQVID(sTag(subsCp).toShort());
Amit Ghosh8951f042017-08-10 13:48:10 +0100737 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
738 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100739
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000740 if (option82) {
741 udpPacket.setPayload(removeOption82(dhcpPayload));
742 } else {
743 udpPacket.setPayload(dhcpPayload);
744 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100745 ipv4Packet.setPayload(udpPacket);
746 etherReply.setPayload(ipv4Packet);
747
748 log.info("Finished processing packet");
749 return etherReply;
750 }
751
Amit Ghosha17354e2017-08-23 12:56:04 +0100752 /*
753 * Get ConnectPoint of the Client based on it's MAC address
754 */
755 private ConnectPoint getConnectPointOfClient(MacAddress dstMac) {
756 Set<Host> hosts = hostService.getHostsByMac(dstMac);
757 if (hosts == null || hosts.isEmpty()) {
758 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530759 + "relay for dhcp packet from server",
Amit Ghosha17354e2017-08-23 12:56:04 +0100760 dstMac);
761 return null;
762 }
763 for (Host h : hosts) {
764 // if more than one,
765 // find the connect point which has an valid entry in SADIS
766 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
767 h.location().port());
768
769 if (sTag(cp) != VlanId.NONE) {
770 return cp;
771 }
772 }
773
774 return null;
775 }
776
Amit Ghosh47243cb2017-07-26 05:08:53 +0100777 //send the response to the requester host.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530778 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100779 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100780 ConnectPoint subCp = getConnectPointOfClient(descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100781
782 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +0100783 if (subCp != null) {
784 log.info("Sending DHCP packet to cp: {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100785 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +0100786 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100787 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +0100788 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100789 if (log.isTraceEnabled()) {
790 log.trace("Relaying packet to dhcp client {}", ethPacket);
791 }
792 packetService.emit(o);
Amit Ghosha17354e2017-08-23 12:56:04 +0100793 log.info("DHCP Packet sent to {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100794 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000795 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100796 }
797 }
798 }
799
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000800 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
801 log.debug("option82data {} ", entry);
802
803 List<DHCPOption> options = Lists.newArrayList(dhcpPacket.getOptions());
804 DhcpOption82 option82 = new DhcpOption82();
805 option82.setAgentCircuitId(entry.circuitId());
806 option82.setAgentRemoteId(entry.remoteId());
807 DHCPOption option = new DHCPOption()
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530808 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
809 .setData(option82.toByteArray())
810 .setLength(option82.length());
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000811
812 options.add(options.size() - 1, option);
813 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +0100814
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000815 return dhcpPacket;
816
817 }
818
819 private DHCP removeOption82(DHCP dhcpPacket) {
820 List<DHCPOption> options = dhcpPacket.getOptions();
821 List<DHCPOption> newoptions = options.stream()
822 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
823 .collect(Collectors.toList());
824
825 return dhcpPacket.setOptions(newoptions);
826 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100827 /**
828 * Listener for network config events.
829 */
830 private class InternalConfigListener implements NetworkConfigListener {
831
832 @Override
833 public void event(NetworkConfigEvent event) {
834
835 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
836 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
837 event.configClass().equals(DhcpL2RelayConfig.class)) {
838 updateConfig();
839 log.info("Reconfigured");
840 }
841 }
842 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000843
Amit Ghosh8951f042017-08-10 13:48:10 +0100844 /**
845 * Handles Mastership changes for the devices which connect
846 * to the DHCP server.
847 */
848 private class InnerMastershipListener implements MastershipListener {
849 @Override
850 public void event(MastershipEvent event) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000851 if (!useOltUplink) {
852 if (dhcpServerConnectPoint.get() != null &&
853 dhcpServerConnectPoint.get().deviceId().
854 equals(event.subject())) {
855 log.trace("Mastership Event recevived for {}", event.subject());
856 // mastership of the device for our connect point has changed
857 // reselect
858 selectServerConnectPoint();
859 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100860 }
861 }
862 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000863
Amit Ghosh8951f042017-08-10 13:48:10 +0100864 /**
865 * Handles Device status change for the devices which connect
866 * to the DHCP server.
867 */
868 private class InnerDeviceListener implements DeviceListener {
869 @Override
870 public void event(DeviceEvent event) {
871 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
Amit Ghosh83c8c892017-11-09 11:08:27 +0000872 if (!useOltUplink) {
873 if (dhcpServerConnectPoint.get() == null) {
874 switch (event.type()) {
875 case DEVICE_ADDED:
876 case DEVICE_AVAILABILITY_CHANGED:
877 // some device is available check if we can get one
878 selectServerConnectPoint();
879 break;
880 default:
881 break;
882 }
883 return;
Amit Ghosh8951f042017-08-10 13:48:10 +0100884 }
Amit Ghosh83c8c892017-11-09 11:08:27 +0000885 if (dhcpServerConnectPoint.get().deviceId().
886 equals(event.subject().id())) {
887 switch (event.type()) {
888 case DEVICE_AVAILABILITY_CHANGED:
889 case DEVICE_REMOVED:
890 case DEVICE_SUSPENDED:
891 // state of our device has changed, check if we need
892 // to re-select
893 selectServerConnectPoint();
894 break;
895 default:
896 break;
897 }
898 }
899 } else {
Amit Ghosh8951f042017-08-10 13:48:10 +0100900 switch (event.type()) {
Amit Ghosh83c8c892017-11-09 11:08:27 +0000901 case PORT_ADDED:
902 if (isUplinkPortOfOlt(event.subject().id(), event.port())) {
903 requestDhcpPacketsFromConnectPoint(new ConnectPoint(event.subject().id(),
904 event.port().number()));
905 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100906 break;
907 default:
908 break;
909 }
910 }
911 }
912 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100913}