blob: f941afc1bf4640f2031297fe7cff40ce1967ce90 [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;
Amit Ghosha17354e2017-08-23 12:56:04 +010019import com.google.common.collect.Maps;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000020import com.google.common.collect.Lists;
Amit Ghosh8951f042017-08-10 13:48:10 +010021import com.google.common.collect.Sets;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000022
Amit Ghosh47243cb2017-07-26 05:08:53 +010023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Modified;
27import org.apache.felix.scr.annotations.Property;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000030import org.onlab.packet.DeserializationException;
Amit Ghosh47243cb2017-07-26 05:08:53 +010031import org.onlab.packet.DHCP;
32import org.onlab.packet.DHCPOption;
33import org.onlab.packet.DHCPPacketType;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000034import org.onlab.packet.Ethernet;
Amit Ghosha17354e2017-08-23 12:56:04 +010035import org.onlab.packet.IpAddress;
Amit Ghosh47243cb2017-07-26 05:08:53 +010036import org.onlab.packet.IPv4;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000037import org.onlab.packet.MacAddress;
Amit Ghosh47243cb2017-07-26 05:08:53 +010038import org.onlab.packet.TpPort;
39import org.onlab.packet.UDP;
40import org.onlab.packet.VlanId;
Amit Ghosh8951f042017-08-10 13:48:10 +010041import org.onosproject.mastership.MastershipEvent;
42import org.onosproject.mastership.MastershipListener;
43import org.onosproject.mastership.MastershipService;
44import org.onosproject.net.device.DeviceEvent;
45import org.onosproject.net.device.DeviceListener;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000046import org.opencord.dhcpl2relay.packet.DhcpEthernet;
47import org.opencord.dhcpl2relay.packet.DhcpOption82;
Amit Ghosh47243cb2017-07-26 05:08:53 +010048import org.onlab.util.Tools;
49import org.onosproject.cfg.ComponentConfigService;
50import org.onosproject.core.ApplicationId;
51import org.onosproject.core.CoreService;
52import org.onosproject.net.AnnotationKeys;
53import org.onosproject.net.ConnectPoint;
54import org.onosproject.net.Host;
55import org.onosproject.net.Port;
56import org.onosproject.net.config.ConfigFactory;
57import org.onosproject.net.config.NetworkConfigEvent;
58import org.onosproject.net.config.NetworkConfigListener;
59import org.onosproject.net.config.NetworkConfigRegistry;
60import org.onosproject.net.device.DeviceService;
61import org.onosproject.net.flow.DefaultTrafficSelector;
62import org.onosproject.net.flow.DefaultTrafficTreatment;
63import org.onosproject.net.flow.TrafficSelector;
64import org.onosproject.net.flow.TrafficTreatment;
65import org.onosproject.net.host.HostService;
66import org.onosproject.net.packet.DefaultOutboundPacket;
67import org.onosproject.net.packet.OutboundPacket;
68import org.onosproject.net.packet.PacketContext;
69import org.onosproject.net.packet.PacketPriority;
70import org.onosproject.net.packet.PacketProcessor;
71import org.onosproject.net.packet.PacketService;
72
73import org.opencord.sadis.SubscriberAndDeviceInformation;
74import org.opencord.sadis.SubscriberAndDeviceInformationService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010075import org.osgi.service.component.ComponentContext;
76import org.slf4j.Logger;
77import org.slf4j.LoggerFactory;
78
79import java.nio.ByteBuffer;
Amit Ghosh03da90a2017-09-25 20:56:55 +010080import java.util.Arrays;
Amit Ghosh47243cb2017-07-26 05:08:53 +010081import java.util.Dictionary;
Amit Ghosha17354e2017-08-23 12:56:04 +010082import java.util.Map;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000083import java.util.List;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000084import java.util.Set;
Amit Ghosh8951f042017-08-10 13:48:10 +010085import java.util.concurrent.atomic.AtomicReference;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000086import java.util.stream.Collectors;
Amit Ghosh47243cb2017-07-26 05:08:53 +010087
88import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
89import static org.onlab.packet.MacAddress.valueOf;
90import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
91
92/**
93 * DHCP Relay Agent Application Component.
94 */
95@Component(immediate = true)
96public class DhcpL2Relay {
97
98 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
99 private final Logger log = LoggerFactory.getLogger(getClass());
100 private final InternalConfigListener cfgListener =
101 new InternalConfigListener();
102
103 private final Set<ConfigFactory> factories = ImmutableSet.of(
104 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
105 DhcpL2RelayConfig.class,
106 "dhcpl2relay") {
107 @Override
108 public DhcpL2RelayConfig createConfig() {
109 return new DhcpL2RelayConfig();
110 }
111 }
112 );
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected NetworkConfigRegistry cfgService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected CoreService coreService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected PacketService packetService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected HostService hostService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ComponentConfigService componentConfigService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected SubscriberAndDeviceInformationService subsService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected DeviceService deviceService;
134
Amit Ghosh8951f042017-08-10 13:48:10 +0100135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected MastershipService mastershipService;
137
Amit Ghosh47243cb2017-07-26 05:08:53 +0100138 @Property(name = "option82", boolValue = true,
139 label = "Add option 82 to relayed packets")
140 protected boolean option82 = true;
141
Amit Ghosha17354e2017-08-23 12:56:04 +0100142 @Property(name = "enableDhcpBroadcastReplies", boolValue = false,
Amit Ghosh03da90a2017-09-25 20:56:55 +0100143 label = "Ask the DHCP Server to send back replies as L2 broadcast")
Amit Ghosha17354e2017-08-23 12:56:04 +0100144 protected boolean enableDhcpBroadcastReplies = false;
145
Amit Ghosh47243cb2017-07-26 05:08:53 +0100146 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
147 new DhcpRelayPacketProcessor();
148
Amit Ghosh8951f042017-08-10 13:48:10 +0100149 private InnerMastershipListener changeListener = new InnerMastershipListener();
150 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100151
Amit Ghosh8951f042017-08-10 13:48:10 +0100152 // connect points to the DHCP server
153 Set<ConnectPoint> dhcpConnectPoints;
154 private AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100155 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
156 private ApplicationId appId;
157
Amit Ghosha17354e2017-08-23 12:56:04 +0100158 static Map<String, DhcpAllocationInfo> allocationMap = Maps.newConcurrentMap();
Amit Ghosh1125d932017-09-25 21:08:31 +0100159 private boolean modifyClientPktsSrcDstMac = false;
Amit Ghosha17354e2017-08-23 12:56:04 +0100160
Amit Ghosh47243cb2017-07-26 05:08:53 +0100161 @Activate
162 protected void activate(ComponentContext context) {
163 //start the dhcp relay agent
164 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
165 componentConfigService.registerProperties(getClass());
166
167 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100168 mastershipService.addListener(changeListener);
169 deviceService.addListener(deviceListener);
170
Amit Ghosh47243cb2017-07-26 05:08:53 +0100171 factories.forEach(cfgService::registerConfigFactory);
172 //update the dhcp server configuration.
173 updateConfig();
174 //add the packet services.
175 packetService.addProcessor(dhcpRelayPacketProcessor,
176 PacketProcessor.director(0));
177 requestDhcpPackets();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000178 if (context != null) {
179 modified(context);
180 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100181
182 log.info("DHCP-L2-RELAY Started");
183 }
184
185 @Deactivate
186 protected void deactivate() {
187 cfgService.removeListener(cfgListener);
188 factories.forEach(cfgService::unregisterConfigFactory);
189 packetService.removeProcessor(dhcpRelayPacketProcessor);
190 cancelDhcpPackets();
191
192 componentConfigService.unregisterProperties(getClass(), false);
Deepa Vaddireddy81728d52017-09-20 20:36:52 +0530193 deviceService.removeListener(deviceListener);
194 mastershipService.removeListener(changeListener);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100195 log.info("DHCP-L2-RELAY Stopped");
196 }
197
198 @Modified
199 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000200
Amit Ghosh47243cb2017-07-26 05:08:53 +0100201 Dictionary<?, ?> properties = context.getProperties();
202
203 Boolean o = Tools.isPropertyEnabled(properties, "option82");
204 if (o != null) {
205 option82 = o;
206 }
Amit Ghosh03da90a2017-09-25 20:56:55 +0100207
208 o = Tools.isPropertyEnabled(properties, "enableDhcpBroadcastReplies");
209 if (o != null) {
210 enableDhcpBroadcastReplies = o;
211 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100212 }
213
214 /**
215 * Checks if this app has been configured.
216 *
217 * @return true if all information we need have been initialized
218 */
219 private boolean configured() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100220 return dhcpServerConnectPoint.get() != null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100221 }
222
Amit Ghosh8951f042017-08-10 13:48:10 +0100223 /**
224 * Selects a connect point through an available device for which it is the master.
225 */
226 private void selectServerConnectPoint() {
227 synchronized (this) {
228 dhcpServerConnectPoint.set(null);
229 if (dhcpConnectPoints != null) {
230 // find a connect point through a device for which we are master
231 for (ConnectPoint cp: dhcpConnectPoints) {
232 if (mastershipService.isLocalMaster(cp.deviceId())) {
233 if (deviceService.isAvailable(cp.deviceId())) {
234 dhcpServerConnectPoint.set(cp);
235 }
236 log.info("DHCP connectPoint selected is {}", cp);
237 break;
238 }
239 }
240 }
241
242 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
243
244 if (dhcpServerConnectPoint.get() == null) {
245 log.error("Master of none, can't relay DHCP Message to server");
246 }
247 }
248 }
249
250 /**
251 * Updates the network configuration.
252 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100253 private void updateConfig() {
254 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
255 if (cfg == null) {
256 log.warn("Dhcp Server info not available");
257 return;
258 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100259
260 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
Amit Ghosh1125d932017-09-25 21:08:31 +0100261 modifyClientPktsSrcDstMac = cfg.getModifySrcDstMacAddresses();
Amit Ghosh8951f042017-08-10 13:48:10 +0100262
263 selectServerConnectPoint();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100264
Amit Ghosh47243cb2017-07-26 05:08:53 +0100265 log.info("dhcp server connect point: " + dhcpServerConnectPoint);
266 }
267
268 /**
269 * Request DHCP packet in via PacketService.
270 */
271 private void requestDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100272 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
273 .matchEthType(Ethernet.TYPE_IPV4)
274 .matchIPProtocol(IPv4.PROTOCOL_UDP)
275 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
276 packetService.requestPackets(selectorServer.build(),
277 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100278
Amit Ghosh8951f042017-08-10 13:48:10 +0100279 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
280 .matchEthType(Ethernet.TYPE_IPV4)
281 .matchIPProtocol(IPv4.PROTOCOL_UDP)
282 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
283 packetService.requestPackets(selectorClient.build(),
284 PacketPriority.CONTROL, appId);
285
Amit Ghosh47243cb2017-07-26 05:08:53 +0100286 }
287
288 /**
289 * Cancel requested DHCP packets in via packet service.
290 */
291 private void cancelDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100292 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
293 .matchEthType(Ethernet.TYPE_IPV4)
294 .matchIPProtocol(IPv4.PROTOCOL_UDP)
295 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
296 packetService.cancelPackets(selectorServer.build(),
297 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100298
Amit Ghosh8951f042017-08-10 13:48:10 +0100299 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
300 .matchEthType(Ethernet.TYPE_IPV4)
301 .matchIPProtocol(IPv4.PROTOCOL_UDP)
302 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
303 packetService.cancelPackets(selectorClient.build(),
304 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100305 }
306
Amit Ghosha17354e2017-08-23 12:56:04 +0100307 public static Map<String, DhcpAllocationInfo> allocationMap() {
308 return allocationMap;
309 }
310
Amit Ghosh47243cb2017-07-26 05:08:53 +0100311 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
312 String serialNo = deviceService.getDevice(context.inPacket().
313 receivedFrom().deviceId()).serialNumber();
314
315 return subsService.get(serialNo);
316 }
317
318 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
319 String serialNo = deviceService.getDevice(cp.deviceId()).
320 serialNumber();
321
322 return subsService.get(serialNo);
323 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100324
325 private MacAddress relayAgentMacAddress(PacketContext context) {
326
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000327 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100328 if (device == null) {
329 log.warn("Device not found for {}", context.inPacket().
330 receivedFrom());
331 return null;
332 }
333
334 return device.hardwareIdentifier();
335 }
336
337 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100338 return nasPortId(context.inPacket().receivedFrom());
339 }
340
341 private String nasPortId(ConnectPoint cp) {
342 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100343 return p.annotations().value(AnnotationKeys.PORT_NAME);
344 }
345
346 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100347 return subsService.get(nasPortId(context));
348 }
349
350 private VlanId cTag(PacketContext context) {
351 SubscriberAndDeviceInformation sub = getSubscriber(context);
352 if (sub == null) {
353 log.warn("Subscriber info not found for {}", context.inPacket().
354 receivedFrom());
355 return VlanId.NONE;
356 }
357 return sub.cTag();
358 }
359
Amit Ghosh8951f042017-08-10 13:48:10 +0100360 private VlanId cTag(ConnectPoint cp) {
361 String portId = nasPortId(cp);
362 SubscriberAndDeviceInformation sub = subsService.get(portId);
363 if (sub == null) {
364 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
365 return VlanId.NONE;
366 }
367 return sub.cTag();
368 }
369
370 private VlanId sTag(ConnectPoint cp) {
371 String portId = nasPortId(cp);
372 SubscriberAndDeviceInformation sub = subsService.get(portId);
373 if (sub == null) {
374 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
375 return VlanId.NONE;
376 }
377 return sub.sTag();
378 }
379
Amit Ghosh47243cb2017-07-26 05:08:53 +0100380 private VlanId sTag(PacketContext context) {
381 SubscriberAndDeviceInformation sub = getSubscriber(context);
382 if (sub == null) {
383 log.warn("Subscriber info not found for {}", context.inPacket().
384 receivedFrom());
385 return VlanId.NONE;
386 }
387 return sub.sTag();
388 }
389
390 private class DhcpRelayPacketProcessor implements PacketProcessor {
391
392 @Override
393 public void process(PacketContext context) {
394 if (!configured()) {
395 log.warn("Missing DHCP relay config. Abort packet processing");
396 return;
397 }
398
399 // process the packet and get the payload
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000400 DhcpEthernet packet = null;
401 ByteBuffer byteBuffer = context.inPacket().unparsed();
402 try {
403 packet = DhcpEthernet.deserializer().deserialize(byteBuffer.array(), 0, byteBuffer.array().length);
404 } catch (DeserializationException e) {
405 log.warn("Unable to deserialize packet");
406 }
407
Amit Ghosh47243cb2017-07-26 05:08:53 +0100408 if (packet == null) {
409 log.warn("Packet is null");
410 return;
411 }
412
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000413 log.debug("Got a packet ", packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100414
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000415 if (packet.getEtherType() == DhcpEthernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100416 IPv4 ipv4Packet = (IPv4) packet.getPayload();
417
418 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
419 UDP udpPacket = (UDP) ipv4Packet.getPayload();
420 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000421 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100422 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
423 //This packet is dhcp.
424 processDhcpPacket(context, packet, dhcpPayload);
425 }
426 }
427 }
428 }
429
430 //forward the packet to ConnectPoint where the DHCP server is attached.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000431 private void forwardPacket(DhcpEthernet packet) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100432
Amit Ghosh8951f042017-08-10 13:48:10 +0100433 if (dhcpServerConnectPoint.get() != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100434 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh8951f042017-08-10 13:48:10 +0100435 .setOutput(dhcpServerConnectPoint.get().port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100436 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh8951f042017-08-10 13:48:10 +0100437 dhcpServerConnectPoint.get().deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100438 ByteBuffer.wrap(packet.serialize()));
439 if (log.isTraceEnabled()) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000440 log.trace("Relaying packet to dhcp server {} at {}",
Amit Ghosh8951f042017-08-10 13:48:10 +0100441 packet, dhcpServerConnectPoint.get());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100442 }
443 packetService.emit(o);
444 } else {
445 log.warn("No dhcp server connect point");
446 }
447 }
448
Amit Ghosha17354e2017-08-23 12:56:04 +0100449 // get the type of the DHCP packet
450 private DHCPPacketType getDhcpPacketType(DHCP dhcpPayload) {
451
452 for (DHCPOption option : dhcpPayload.getOptions()) {
453 if (option.getCode() == OptionCode_MessageType.getValue()) {
454 byte[] data = option.getData();
455 return DHCPPacketType.getType(data[0]);
456 }
457 }
458 return null;
459 }
460
Amit Ghosh47243cb2017-07-26 05:08:53 +0100461 //process the dhcp packet before sending to server
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000462 private void processDhcpPacket(PacketContext context, DhcpEthernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100463 DHCP dhcpPayload) {
464 if (dhcpPayload == null) {
465 log.warn("DHCP payload is null");
466 return;
467 }
468
Amit Ghosha17354e2017-08-23 12:56:04 +0100469 DHCPPacketType incomingPacketType = getDhcpPacketType(dhcpPayload);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000470
Amit Ghosh47243cb2017-07-26 05:08:53 +0100471 log.info("Received DHCP Packet of type {}", incomingPacketType);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100472
473 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000474 case DHCPDISCOVER:
475 DhcpEthernet ethernetPacketDiscover =
476 processDhcpPacketFromClient(context, packet);
477 if (ethernetPacketDiscover != null) {
478 forwardPacket(ethernetPacketDiscover);
479 }
480 break;
481 case DHCPOFFER:
482 //reply to dhcp client.
483 DhcpEthernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
484 if (ethernetPacketOffer != null) {
485 sendReply(ethernetPacketOffer, dhcpPayload);
486 }
487 break;
488 case DHCPREQUEST:
489 DhcpEthernet ethernetPacketRequest =
490 processDhcpPacketFromClient(context, packet);
491 if (ethernetPacketRequest != null) {
492 forwardPacket(ethernetPacketRequest);
493 }
494 break;
495 case DHCPACK:
496 //reply to dhcp client.
497 DhcpEthernet ethernetPacketAck = processDhcpPacketFromServer(packet);
498 if (ethernetPacketAck != null) {
499 sendReply(ethernetPacketAck, dhcpPayload);
500 }
501 break;
502 default:
503 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100504 }
505 }
506
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000507 private DhcpEthernet processDhcpPacketFromClient(PacketContext context,
508 DhcpEthernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100509
510 MacAddress relayAgentMac = relayAgentMacAddress(context);
511 if (relayAgentMac == null) {
512 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100513 return null;
514 }
515
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000516 DhcpEthernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100517
518 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
519 UDP udpPacket = (UDP) ipv4Packet.getPayload();
520 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
521
Amit Ghosha17354e2017-08-23 12:56:04 +0100522 if (enableDhcpBroadcastReplies) {
523 // We want the reply to come back as a L2 broadcast
524 dhcpPacket.setFlags((short) 0x8000);
525 }
526
527 // remove from the allocation map (used for display) as it's is
528 // requesting a fresh allocation
529 if (getDhcpPacketType(dhcpPacket) == DHCPPacketType.DHCPREQUEST) {
530
531 String portId = nasPortId(context.inPacket().receivedFrom());
532 SubscriberAndDeviceInformation sub = subsService.get(portId);
533 if (sub != null) {
534 allocationMap.remove(sub.nasPortId());
535 }
536 } // end allocation for display
537
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000538 if (option82) {
539 SubscriberAndDeviceInformation entry = getSubscriber(context);
540 if (entry == null) {
541 log.info("Dropping packet as subscriber entry is not available");
542 return null;
543 }
544
545 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
546 udpPacket.setPayload(dhcpPacketWithOption82);
547 }
548
549 ipv4Packet.setPayload(udpPacket);
550 etherReply.setPayload(ipv4Packet);
Amit Ghosh1125d932017-09-25 21:08:31 +0100551 if (modifyClientPktsSrcDstMac) {
552 etherReply.setSourceMacAddress(relayAgentMac);
553 etherReply.setDestinationMacAddress(dhcpConnectMac);
554 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100555
Amit Ghosh8951f042017-08-10 13:48:10 +0100556 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100557 etherReply.setVlanID(cTag(context).toShort());
Amit Ghosha17354e2017-08-23 12:56:04 +0100558 etherReply.setQinQtpid(DhcpEthernet.TYPE_VLAN);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000559 etherReply.setQinQVid(sTag(context).toShort());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100560
Amit Ghosh8951f042017-08-10 13:48:10 +0100561 log.info("Finished processing packet -- sending packet {}", etherReply);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100562 return etherReply;
563 }
564
565 //build the DHCP offer/ack with proper client port.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000566 private DhcpEthernet processDhcpPacketFromServer(DhcpEthernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100567 // get dhcp header.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000568 DhcpEthernet etherReply = (DhcpEthernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100569 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
570 UDP udpPacket = (UDP) ipv4Packet.getPayload();
571 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
572
Amit Ghosh47243cb2017-07-26 05:08:53 +0100573 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100574 ConnectPoint subsCp = getConnectPointOfClient(dstMac);
Amit Ghosh03da90a2017-09-25 20:56:55 +0100575 // If we can't find the subscriber, can't process further
576 if (subsCp == null) {
577 return null;
578 }
Amit Ghosha17354e2017-08-23 12:56:04 +0100579 // if it's an ACK packet store the information for display purpose
580 if (getDhcpPacketType(dhcpPayload) == DHCPPacketType.DHCPACK) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100581
Amit Ghosha17354e2017-08-23 12:56:04 +0100582 String portId = nasPortId(subsCp);
583 SubscriberAndDeviceInformation sub = subsService.get(portId);
584 if (sub != null) {
585 List<DHCPOption> options = dhcpPayload.getOptions();
586 List<DHCPOption> circuitIds = options.stream()
587 .filter(option -> option.getCode() == DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
588 .collect(Collectors.toList());
589
590 String circuitId = "None";
591 if (circuitIds.size() == 1) {
Amit Ghosh03da90a2017-09-25 20:56:55 +0100592 byte[] array = circuitIds.get(0).getData();
593
594 try {
595 // we leave the first two bytes as they are the id and length
596 circuitId = new String(Arrays.copyOfRange(array, 2, array.length), "UTF-8");
597 } catch (Exception e) { }
Amit Ghosha17354e2017-08-23 12:56:04 +0100598 }
599
600 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
601
602 //storeDHCPAllocationInfo
603 DhcpAllocationInfo info = new DhcpAllocationInfo(circuitId, dstMac, ip);
604
605 allocationMap.put(sub.nasPortId(), info);
606 }
607 } // end storing of info
Amit Ghosh47243cb2017-07-26 05:08:53 +0100608
609 // we leave the srcMac from the original packet
Amit Ghosh8951f042017-08-10 13:48:10 +0100610 etherReply.setDestinationMacAddress(dstMac);
611 etherReply.setQinQVid(sTag(subsCp).toShort());
612 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
613 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100614
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000615 if (option82) {
616 udpPacket.setPayload(removeOption82(dhcpPayload));
617 } else {
618 udpPacket.setPayload(dhcpPayload);
619 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100620 ipv4Packet.setPayload(udpPacket);
621 etherReply.setPayload(ipv4Packet);
622
623 log.info("Finished processing packet");
624 return etherReply;
625 }
626
Amit Ghosha17354e2017-08-23 12:56:04 +0100627 /*
628 * Get ConnectPoint of the Client based on it's MAC address
629 */
630 private ConnectPoint getConnectPointOfClient(MacAddress dstMac) {
631 Set<Host> hosts = hostService.getHostsByMac(dstMac);
632 if (hosts == null || hosts.isEmpty()) {
633 log.warn("Cannot determine host for DHCP client: {}. Aborting "
634 + "relay for dhcp packet from server",
635 dstMac);
636 return null;
637 }
638 for (Host h : hosts) {
639 // if more than one,
640 // find the connect point which has an valid entry in SADIS
641 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
642 h.location().port());
643
644 if (sTag(cp) != VlanId.NONE) {
645 return cp;
646 }
647 }
648
649 return null;
650 }
651
Amit Ghosh47243cb2017-07-26 05:08:53 +0100652 //send the response to the requester host.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000653 private void sendReply(DhcpEthernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100654 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100655 ConnectPoint subCp = getConnectPointOfClient(descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100656
657 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +0100658 if (subCp != null) {
659 log.info("Sending DHCP packet to cp: {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100660 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +0100661 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100662 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +0100663 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100664 if (log.isTraceEnabled()) {
665 log.trace("Relaying packet to dhcp client {}", ethPacket);
666 }
667 packetService.emit(o);
Amit Ghosha17354e2017-08-23 12:56:04 +0100668 log.info("DHCP Packet sent to {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100669 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000670 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100671 }
672 }
673 }
674
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000675 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
676 log.debug("option82data {} ", entry);
677
678 List<DHCPOption> options = Lists.newArrayList(dhcpPacket.getOptions());
679 DhcpOption82 option82 = new DhcpOption82();
680 option82.setAgentCircuitId(entry.circuitId());
681 option82.setAgentRemoteId(entry.remoteId());
682 DHCPOption option = new DHCPOption()
683 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
684 .setData(option82.toByteArray())
685 .setLength(option82.length());
686
687 options.add(options.size() - 1, option);
688 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +0100689
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000690 return dhcpPacket;
691
692 }
693
694 private DHCP removeOption82(DHCP dhcpPacket) {
695 List<DHCPOption> options = dhcpPacket.getOptions();
696 List<DHCPOption> newoptions = options.stream()
697 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
698 .collect(Collectors.toList());
699
700 return dhcpPacket.setOptions(newoptions);
701 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100702 /**
703 * Listener for network config events.
704 */
705 private class InternalConfigListener implements NetworkConfigListener {
706
707 @Override
708 public void event(NetworkConfigEvent event) {
709
710 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
711 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
712 event.configClass().equals(DhcpL2RelayConfig.class)) {
713 updateConfig();
714 log.info("Reconfigured");
715 }
716 }
717 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000718
Amit Ghosh8951f042017-08-10 13:48:10 +0100719 /**
720 * Handles Mastership changes for the devices which connect
721 * to the DHCP server.
722 */
723 private class InnerMastershipListener implements MastershipListener {
724 @Override
725 public void event(MastershipEvent event) {
726 if (dhcpServerConnectPoint.get() != null &&
727 dhcpServerConnectPoint.get().deviceId().
728 equals(event.subject())) {
729 log.trace("Mastership Event recevived for {}", event.subject());
730 // mastership of the device for our connect point has changed
731 // reselect
732 selectServerConnectPoint();
733 }
734 }
735 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000736
Amit Ghosh8951f042017-08-10 13:48:10 +0100737 /**
738 * Handles Device status change for the devices which connect
739 * to the DHCP server.
740 */
741 private class InnerDeviceListener implements DeviceListener {
742 @Override
743 public void event(DeviceEvent event) {
744 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
745 if (dhcpServerConnectPoint.get() == null) {
746 switch (event.type()) {
747 case DEVICE_ADDED:
748 case DEVICE_AVAILABILITY_CHANGED:
749 // some device is available check if we can get one
750 selectServerConnectPoint();
751 break;
752 default:
753 break;
754 }
755 return;
756 }
757 if (dhcpServerConnectPoint.get().deviceId().
758 equals(event.subject().id())) {
759 switch (event.type()) {
760 case DEVICE_AVAILABILITY_CHANGED:
761 case DEVICE_REMOVED:
762 case DEVICE_SUSPENDED:
763 // state of our device has changed, check if we need
764 // to re-select
765 selectServerConnectPoint();
766 break;
767 default:
768 break;
769 }
770 }
771 }
772 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100773}