blob: 551608649ca878b95329ce93de292e7394813f2a [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;
48import org.onosproject.net.Host;
49import org.onosproject.net.Port;
50import org.onosproject.net.config.ConfigFactory;
51import org.onosproject.net.config.NetworkConfigEvent;
52import org.onosproject.net.config.NetworkConfigListener;
53import org.onosproject.net.config.NetworkConfigRegistry;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053054import org.onosproject.net.device.DeviceEvent;
55import org.onosproject.net.device.DeviceListener;
Amit Ghosh47243cb2017-07-26 05:08:53 +010056import org.onosproject.net.device.DeviceService;
57import org.onosproject.net.flow.DefaultTrafficSelector;
58import org.onosproject.net.flow.DefaultTrafficTreatment;
59import org.onosproject.net.flow.TrafficSelector;
60import org.onosproject.net.flow.TrafficTreatment;
61import org.onosproject.net.host.HostService;
62import org.onosproject.net.packet.DefaultOutboundPacket;
63import org.onosproject.net.packet.OutboundPacket;
64import org.onosproject.net.packet.PacketContext;
65import org.onosproject.net.packet.PacketPriority;
66import org.onosproject.net.packet.PacketProcessor;
67import org.onosproject.net.packet.PacketService;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053068import org.opencord.dhcpl2relay.packet.DhcpOption82;
Amit Ghosh47243cb2017-07-26 05:08:53 +010069import org.opencord.sadis.SubscriberAndDeviceInformation;
70import org.opencord.sadis.SubscriberAndDeviceInformationService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010071import org.osgi.service.component.ComponentContext;
72import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
75import java.nio.ByteBuffer;
Amit Ghosh2095dc62017-09-25 20:56:55 +010076import java.util.Arrays;
Amit Ghosh47243cb2017-07-26 05:08:53 +010077import java.util.Dictionary;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000078import java.util.List;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053079import java.util.Map;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000080import java.util.Set;
Amit Ghosh8951f042017-08-10 13:48:10 +010081import java.util.concurrent.atomic.AtomicReference;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000082import java.util.stream.Collectors;
Amit Ghosh47243cb2017-07-26 05:08:53 +010083
84import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
85import static org.onlab.packet.MacAddress.valueOf;
86import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
87
88/**
89 * DHCP Relay Agent Application Component.
90 */
91@Component(immediate = true)
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053092public class
93DhcpL2Relay {
Amit Ghosh47243cb2017-07-26 05:08:53 +010094
95 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
96 private final Logger log = LoggerFactory.getLogger(getClass());
97 private final InternalConfigListener cfgListener =
98 new InternalConfigListener();
99
100 private final Set<ConfigFactory> factories = ImmutableSet.of(
101 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
102 DhcpL2RelayConfig.class,
103 "dhcpl2relay") {
104 @Override
105 public DhcpL2RelayConfig createConfig() {
106 return new DhcpL2RelayConfig();
107 }
108 }
109 );
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected NetworkConfigRegistry cfgService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected CoreService coreService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected PacketService packetService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected HostService hostService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected ComponentConfigService componentConfigService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected SubscriberAndDeviceInformationService subsService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected DeviceService deviceService;
131
Amit Ghosh8951f042017-08-10 13:48:10 +0100132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected MastershipService mastershipService;
134
Amit Ghosh47243cb2017-07-26 05:08:53 +0100135 @Property(name = "option82", boolValue = true,
136 label = "Add option 82 to relayed packets")
137 protected boolean option82 = true;
138
Amit Ghosha17354e2017-08-23 12:56:04 +0100139 @Property(name = "enableDhcpBroadcastReplies", boolValue = false,
Amit Ghosh2095dc62017-09-25 20:56:55 +0100140 label = "Ask the DHCP Server to send back replies as L2 broadcast")
Amit Ghosha17354e2017-08-23 12:56:04 +0100141 protected boolean enableDhcpBroadcastReplies = false;
142
Amit Ghosh47243cb2017-07-26 05:08:53 +0100143 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
144 new DhcpRelayPacketProcessor();
145
Amit Ghosh8951f042017-08-10 13:48:10 +0100146 private InnerMastershipListener changeListener = new InnerMastershipListener();
147 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100148
Amit Ghosh8951f042017-08-10 13:48:10 +0100149 // connect points to the DHCP server
150 Set<ConnectPoint> dhcpConnectPoints;
151 private AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100152 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
153 private ApplicationId appId;
154
Amit Ghosha17354e2017-08-23 12:56:04 +0100155 static Map<String, DhcpAllocationInfo> allocationMap = Maps.newConcurrentMap();
156
Amit Ghosh47243cb2017-07-26 05:08:53 +0100157 @Activate
158 protected void activate(ComponentContext context) {
159 //start the dhcp relay agent
160 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
161 componentConfigService.registerProperties(getClass());
162
163 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100164 mastershipService.addListener(changeListener);
165 deviceService.addListener(deviceListener);
166
Amit Ghosh47243cb2017-07-26 05:08:53 +0100167 factories.forEach(cfgService::registerConfigFactory);
168 //update the dhcp server configuration.
169 updateConfig();
170 //add the packet services.
171 packetService.addProcessor(dhcpRelayPacketProcessor,
172 PacketProcessor.director(0));
173 requestDhcpPackets();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000174 if (context != null) {
175 modified(context);
176 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100177
178 log.info("DHCP-L2-RELAY Started");
179 }
180
181 @Deactivate
182 protected void deactivate() {
183 cfgService.removeListener(cfgListener);
184 factories.forEach(cfgService::unregisterConfigFactory);
185 packetService.removeProcessor(dhcpRelayPacketProcessor);
186 cancelDhcpPackets();
187
188 componentConfigService.unregisterProperties(getClass(), false);
Deepa Vaddireddy77a6ac72017-09-20 20:36:52 +0530189 deviceService.removeListener(deviceListener);
190 mastershipService.removeListener(changeListener);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100191 log.info("DHCP-L2-RELAY Stopped");
192 }
193
194 @Modified
195 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000196
Amit Ghosh47243cb2017-07-26 05:08:53 +0100197 Dictionary<?, ?> properties = context.getProperties();
198
199 Boolean o = Tools.isPropertyEnabled(properties, "option82");
200 if (o != null) {
201 option82 = o;
202 }
Amit Ghosh2095dc62017-09-25 20:56:55 +0100203
204 o = Tools.isPropertyEnabled(properties, "enableDhcpBroadcastReplies");
205 if (o != null) {
206 enableDhcpBroadcastReplies = o;
207 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100208 }
209
210 /**
211 * Checks if this app has been configured.
212 *
213 * @return true if all information we need have been initialized
214 */
215 private boolean configured() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100216 return dhcpServerConnectPoint.get() != null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100217 }
218
Amit Ghosh8951f042017-08-10 13:48:10 +0100219 /**
220 * Selects a connect point through an available device for which it is the master.
221 */
222 private void selectServerConnectPoint() {
223 synchronized (this) {
224 dhcpServerConnectPoint.set(null);
225 if (dhcpConnectPoints != null) {
226 // find a connect point through a device for which we are master
227 for (ConnectPoint cp: dhcpConnectPoints) {
228 if (mastershipService.isLocalMaster(cp.deviceId())) {
229 if (deviceService.isAvailable(cp.deviceId())) {
230 dhcpServerConnectPoint.set(cp);
231 }
232 log.info("DHCP connectPoint selected is {}", cp);
233 break;
234 }
235 }
236 }
237
238 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
239
240 if (dhcpServerConnectPoint.get() == null) {
241 log.error("Master of none, can't relay DHCP Message to server");
242 }
243 }
244 }
245
246 /**
247 * Updates the network configuration.
248 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100249 private void updateConfig() {
250 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
251 if (cfg == null) {
252 log.warn("Dhcp Server info not available");
253 return;
254 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100255
256 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
257
258 selectServerConnectPoint();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100259
Amit Ghosh47243cb2017-07-26 05:08:53 +0100260 log.info("dhcp server connect point: " + dhcpServerConnectPoint);
261 }
262
263 /**
264 * Request DHCP packet in via PacketService.
265 */
266 private void requestDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100267 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
268 .matchEthType(Ethernet.TYPE_IPV4)
269 .matchIPProtocol(IPv4.PROTOCOL_UDP)
270 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
271 packetService.requestPackets(selectorServer.build(),
272 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100273
Amit Ghosh8951f042017-08-10 13:48:10 +0100274 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
275 .matchEthType(Ethernet.TYPE_IPV4)
276 .matchIPProtocol(IPv4.PROTOCOL_UDP)
277 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
278 packetService.requestPackets(selectorClient.build(),
279 PacketPriority.CONTROL, appId);
280
Amit Ghosh47243cb2017-07-26 05:08:53 +0100281 }
282
283 /**
284 * Cancel requested DHCP packets in via packet service.
285 */
286 private void cancelDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100287 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
288 .matchEthType(Ethernet.TYPE_IPV4)
289 .matchIPProtocol(IPv4.PROTOCOL_UDP)
290 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
291 packetService.cancelPackets(selectorServer.build(),
292 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100293
Amit Ghosh8951f042017-08-10 13:48:10 +0100294 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
295 .matchEthType(Ethernet.TYPE_IPV4)
296 .matchIPProtocol(IPv4.PROTOCOL_UDP)
297 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
298 packetService.cancelPackets(selectorClient.build(),
299 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100300 }
301
Amit Ghosha17354e2017-08-23 12:56:04 +0100302 public static Map<String, DhcpAllocationInfo> allocationMap() {
303 return allocationMap;
304 }
305
Amit Ghosh47243cb2017-07-26 05:08:53 +0100306 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
307 String serialNo = deviceService.getDevice(context.inPacket().
308 receivedFrom().deviceId()).serialNumber();
309
310 return subsService.get(serialNo);
311 }
312
313 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
314 String serialNo = deviceService.getDevice(cp.deviceId()).
315 serialNumber();
316
317 return subsService.get(serialNo);
318 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100319
320 private MacAddress relayAgentMacAddress(PacketContext context) {
321
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000322 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100323 if (device == null) {
324 log.warn("Device not found for {}", context.inPacket().
325 receivedFrom());
326 return null;
327 }
328
329 return device.hardwareIdentifier();
330 }
331
332 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100333 return nasPortId(context.inPacket().receivedFrom());
334 }
335
336 private String nasPortId(ConnectPoint cp) {
337 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100338 return p.annotations().value(AnnotationKeys.PORT_NAME);
339 }
340
341 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100342 return subsService.get(nasPortId(context));
343 }
344
345 private VlanId cTag(PacketContext context) {
346 SubscriberAndDeviceInformation sub = getSubscriber(context);
347 if (sub == null) {
348 log.warn("Subscriber info not found for {}", context.inPacket().
349 receivedFrom());
350 return VlanId.NONE;
351 }
352 return sub.cTag();
353 }
354
Amit Ghosh8951f042017-08-10 13:48:10 +0100355 private VlanId cTag(ConnectPoint cp) {
356 String portId = nasPortId(cp);
357 SubscriberAndDeviceInformation sub = subsService.get(portId);
358 if (sub == null) {
359 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
360 return VlanId.NONE;
361 }
362 return sub.cTag();
363 }
364
365 private VlanId sTag(ConnectPoint cp) {
366 String portId = nasPortId(cp);
367 SubscriberAndDeviceInformation sub = subsService.get(portId);
368 if (sub == null) {
369 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
370 return VlanId.NONE;
371 }
372 return sub.sTag();
373 }
374
Amit Ghosh47243cb2017-07-26 05:08:53 +0100375 private VlanId sTag(PacketContext context) {
376 SubscriberAndDeviceInformation sub = getSubscriber(context);
377 if (sub == null) {
378 log.warn("Subscriber info not found for {}", context.inPacket().
379 receivedFrom());
380 return VlanId.NONE;
381 }
382 return sub.sTag();
383 }
384
385 private class DhcpRelayPacketProcessor implements PacketProcessor {
386
387 @Override
388 public void process(PacketContext context) {
389 if (!configured()) {
390 log.warn("Missing DHCP relay config. Abort packet processing");
391 return;
392 }
393
394 // process the packet and get the payload
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530395 Ethernet packet = context.inPacket().parsed();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000396
Amit Ghosh47243cb2017-07-26 05:08:53 +0100397 if (packet == null) {
398 log.warn("Packet is null");
399 return;
400 }
401
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000402 log.debug("Got a packet ", packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100403
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530404 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100405 IPv4 ipv4Packet = (IPv4) packet.getPayload();
406
407 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
408 UDP udpPacket = (UDP) ipv4Packet.getPayload();
409 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000410 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100411 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
412 //This packet is dhcp.
413 processDhcpPacket(context, packet, dhcpPayload);
414 }
415 }
416 }
417 }
418
419 //forward the packet to ConnectPoint where the DHCP server is attached.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530420 private void forwardPacket(Ethernet packet) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100421
Amit Ghosh8951f042017-08-10 13:48:10 +0100422 if (dhcpServerConnectPoint.get() != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100423 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh8951f042017-08-10 13:48:10 +0100424 .setOutput(dhcpServerConnectPoint.get().port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100425 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh8951f042017-08-10 13:48:10 +0100426 dhcpServerConnectPoint.get().deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100427 ByteBuffer.wrap(packet.serialize()));
428 if (log.isTraceEnabled()) {
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530429 log.trace("Relaying packet to dhcp server {} at {}",
430 packet, dhcpServerConnectPoint.get());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100431 }
432 packetService.emit(o);
433 } else {
434 log.warn("No dhcp server connect point");
435 }
436 }
437
Amit Ghosha17354e2017-08-23 12:56:04 +0100438 // get the type of the DHCP packet
439 private DHCPPacketType getDhcpPacketType(DHCP dhcpPayload) {
440
441 for (DHCPOption option : dhcpPayload.getOptions()) {
442 if (option.getCode() == OptionCode_MessageType.getValue()) {
443 byte[] data = option.getData();
444 return DHCPPacketType.getType(data[0]);
445 }
446 }
447 return null;
448 }
449
Amit Ghosh47243cb2017-07-26 05:08:53 +0100450 //process the dhcp packet before sending to server
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530451 private void processDhcpPacket(PacketContext context, Ethernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100452 DHCP dhcpPayload) {
453 if (dhcpPayload == null) {
454 log.warn("DHCP payload is null");
455 return;
456 }
457
Amit Ghosha17354e2017-08-23 12:56:04 +0100458 DHCPPacketType incomingPacketType = getDhcpPacketType(dhcpPayload);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000459
Amit Ghosh47243cb2017-07-26 05:08:53 +0100460 log.info("Received DHCP Packet of type {}", incomingPacketType);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100461
462 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000463 case DHCPDISCOVER:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530464 Ethernet ethernetPacketDiscover =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000465 processDhcpPacketFromClient(context, packet);
466 if (ethernetPacketDiscover != null) {
467 forwardPacket(ethernetPacketDiscover);
468 }
469 break;
470 case DHCPOFFER:
471 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530472 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000473 if (ethernetPacketOffer != null) {
474 sendReply(ethernetPacketOffer, dhcpPayload);
475 }
476 break;
477 case DHCPREQUEST:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530478 Ethernet ethernetPacketRequest =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000479 processDhcpPacketFromClient(context, packet);
480 if (ethernetPacketRequest != null) {
481 forwardPacket(ethernetPacketRequest);
482 }
483 break;
484 case DHCPACK:
485 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530486 Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000487 if (ethernetPacketAck != null) {
488 sendReply(ethernetPacketAck, dhcpPayload);
489 }
490 break;
491 default:
492 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100493 }
494 }
495
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530496 private Ethernet processDhcpPacketFromClient(PacketContext context,
497 Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100498
499 MacAddress relayAgentMac = relayAgentMacAddress(context);
500 if (relayAgentMac == null) {
501 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100502 return null;
503 }
504
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530505 Ethernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100506
507 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
508 UDP udpPacket = (UDP) ipv4Packet.getPayload();
509 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
510
Amit Ghosha17354e2017-08-23 12:56:04 +0100511 if (enableDhcpBroadcastReplies) {
512 // We want the reply to come back as a L2 broadcast
513 dhcpPacket.setFlags((short) 0x8000);
514 }
515
516 // remove from the allocation map (used for display) as it's is
517 // requesting a fresh allocation
518 if (getDhcpPacketType(dhcpPacket) == DHCPPacketType.DHCPREQUEST) {
519
520 String portId = nasPortId(context.inPacket().receivedFrom());
521 SubscriberAndDeviceInformation sub = subsService.get(portId);
522 if (sub != null) {
523 allocationMap.remove(sub.nasPortId());
524 }
525 } // end allocation for display
526
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000527 if (option82) {
528 SubscriberAndDeviceInformation entry = getSubscriber(context);
529 if (entry == null) {
530 log.info("Dropping packet as subscriber entry is not available");
531 return null;
532 }
533
534 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
535 udpPacket.setPayload(dhcpPacketWithOption82);
536 }
537
538 ipv4Packet.setPayload(udpPacket);
539 etherReply.setPayload(ipv4Packet);
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530540 etherReply.setSourceMACAddress(relayAgentMac);
541 etherReply.setDestinationMACAddress(dhcpConnectMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100542
Amit Ghosh8951f042017-08-10 13:48:10 +0100543 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100544 etherReply.setVlanID(cTag(context).toShort());
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530545 etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
546 etherReply.setQinQVID(sTag(context).toShort());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100547
Amit Ghosh8951f042017-08-10 13:48:10 +0100548 log.info("Finished processing packet -- sending packet {}", etherReply);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100549 return etherReply;
550 }
551
552 //build the DHCP offer/ack with proper client port.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530553 private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100554 // get dhcp header.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530555 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100556 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
557 UDP udpPacket = (UDP) ipv4Packet.getPayload();
558 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
559
Amit Ghosh47243cb2017-07-26 05:08:53 +0100560 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100561 ConnectPoint subsCp = getConnectPointOfClient(dstMac);
Amit Ghosh2095dc62017-09-25 20:56:55 +0100562 // If we can't find the subscriber, can't process further
563 if (subsCp == null) {
564 return null;
565 }
Amit Ghosha17354e2017-08-23 12:56:04 +0100566 // if it's an ACK packet store the information for display purpose
567 if (getDhcpPacketType(dhcpPayload) == DHCPPacketType.DHCPACK) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100568
Amit Ghosha17354e2017-08-23 12:56:04 +0100569 String portId = nasPortId(subsCp);
570 SubscriberAndDeviceInformation sub = subsService.get(portId);
571 if (sub != null) {
572 List<DHCPOption> options = dhcpPayload.getOptions();
573 List<DHCPOption> circuitIds = options.stream()
574 .filter(option -> option.getCode() == DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
575 .collect(Collectors.toList());
576
577 String circuitId = "None";
578 if (circuitIds.size() == 1) {
Amit Ghosh2095dc62017-09-25 20:56:55 +0100579 byte[] array = circuitIds.get(0).getData();
580
581 try {
582 // we leave the first two bytes as they are the id and length
583 circuitId = new String(Arrays.copyOfRange(array, 2, array.length), "UTF-8");
584 } catch (Exception e) { }
Amit Ghosha17354e2017-08-23 12:56:04 +0100585 }
586
587 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
588
589 //storeDHCPAllocationInfo
590 DhcpAllocationInfo info = new DhcpAllocationInfo(circuitId, dstMac, ip);
591
592 allocationMap.put(sub.nasPortId(), info);
593 }
594 } // end storing of info
Amit Ghosh47243cb2017-07-26 05:08:53 +0100595
596 // we leave the srcMac from the original packet
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530597 etherReply.setDestinationMACAddress(dstMac);
598 etherReply.setQinQVID(sTag(subsCp).toShort());
Amit Ghosh8951f042017-08-10 13:48:10 +0100599 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
600 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100601
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000602 if (option82) {
603 udpPacket.setPayload(removeOption82(dhcpPayload));
604 } else {
605 udpPacket.setPayload(dhcpPayload);
606 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100607 ipv4Packet.setPayload(udpPacket);
608 etherReply.setPayload(ipv4Packet);
609
610 log.info("Finished processing packet");
611 return etherReply;
612 }
613
Amit Ghosha17354e2017-08-23 12:56:04 +0100614 /*
615 * Get ConnectPoint of the Client based on it's MAC address
616 */
617 private ConnectPoint getConnectPointOfClient(MacAddress dstMac) {
618 Set<Host> hosts = hostService.getHostsByMac(dstMac);
619 if (hosts == null || hosts.isEmpty()) {
620 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530621 + "relay for dhcp packet from server",
Amit Ghosha17354e2017-08-23 12:56:04 +0100622 dstMac);
623 return null;
624 }
625 for (Host h : hosts) {
626 // if more than one,
627 // find the connect point which has an valid entry in SADIS
628 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
629 h.location().port());
630
631 if (sTag(cp) != VlanId.NONE) {
632 return cp;
633 }
634 }
635
636 return null;
637 }
638
Amit Ghosh47243cb2017-07-26 05:08:53 +0100639 //send the response to the requester host.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530640 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100641 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100642 ConnectPoint subCp = getConnectPointOfClient(descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100643
644 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +0100645 if (subCp != null) {
646 log.info("Sending DHCP packet to cp: {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100647 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +0100648 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100649 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +0100650 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100651 if (log.isTraceEnabled()) {
652 log.trace("Relaying packet to dhcp client {}", ethPacket);
653 }
654 packetService.emit(o);
Amit Ghosha17354e2017-08-23 12:56:04 +0100655 log.info("DHCP Packet sent to {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100656 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000657 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100658 }
659 }
660 }
661
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000662 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
663 log.debug("option82data {} ", entry);
664
665 List<DHCPOption> options = Lists.newArrayList(dhcpPacket.getOptions());
666 DhcpOption82 option82 = new DhcpOption82();
667 option82.setAgentCircuitId(entry.circuitId());
668 option82.setAgentRemoteId(entry.remoteId());
669 DHCPOption option = new DHCPOption()
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530670 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
671 .setData(option82.toByteArray())
672 .setLength(option82.length());
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000673
674 options.add(options.size() - 1, option);
675 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +0100676
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000677 return dhcpPacket;
678
679 }
680
681 private DHCP removeOption82(DHCP dhcpPacket) {
682 List<DHCPOption> options = dhcpPacket.getOptions();
683 List<DHCPOption> newoptions = options.stream()
684 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
685 .collect(Collectors.toList());
686
687 return dhcpPacket.setOptions(newoptions);
688 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100689 /**
690 * Listener for network config events.
691 */
692 private class InternalConfigListener implements NetworkConfigListener {
693
694 @Override
695 public void event(NetworkConfigEvent event) {
696
697 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
698 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
699 event.configClass().equals(DhcpL2RelayConfig.class)) {
700 updateConfig();
701 log.info("Reconfigured");
702 }
703 }
704 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000705
Amit Ghosh8951f042017-08-10 13:48:10 +0100706 /**
707 * Handles Mastership changes for the devices which connect
708 * to the DHCP server.
709 */
710 private class InnerMastershipListener implements MastershipListener {
711 @Override
712 public void event(MastershipEvent event) {
713 if (dhcpServerConnectPoint.get() != null &&
714 dhcpServerConnectPoint.get().deviceId().
715 equals(event.subject())) {
716 log.trace("Mastership Event recevived for {}", event.subject());
717 // mastership of the device for our connect point has changed
718 // reselect
719 selectServerConnectPoint();
720 }
721 }
722 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000723
Amit Ghosh8951f042017-08-10 13:48:10 +0100724 /**
725 * Handles Device status change for the devices which connect
726 * to the DHCP server.
727 */
728 private class InnerDeviceListener implements DeviceListener {
729 @Override
730 public void event(DeviceEvent event) {
731 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
732 if (dhcpServerConnectPoint.get() == null) {
733 switch (event.type()) {
734 case DEVICE_ADDED:
735 case DEVICE_AVAILABILITY_CHANGED:
736 // some device is available check if we can get one
737 selectServerConnectPoint();
738 break;
739 default:
740 break;
741 }
742 return;
743 }
744 if (dhcpServerConnectPoint.get().deviceId().
745 equals(event.subject().id())) {
746 switch (event.type()) {
747 case DEVICE_AVAILABILITY_CHANGED:
748 case DEVICE_REMOVED:
749 case DEVICE_SUSPENDED:
750 // state of our device has changed, check if we need
751 // to re-select
752 selectServerConnectPoint();
753 break;
754 default:
755 break;
756 }
757 }
758 }
759 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100760}