blob: 4f801cf228de9618d6e68400290bbafa4ea0b37c [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;
76import java.util.Dictionary;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000077import java.util.List;
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053078import java.util.Map;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000079import java.util.Set;
Amit Ghosh8951f042017-08-10 13:48:10 +010080import java.util.concurrent.atomic.AtomicReference;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000081import java.util.stream.Collectors;
Amit Ghosh47243cb2017-07-26 05:08:53 +010082
83import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
84import static org.onlab.packet.MacAddress.valueOf;
85import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
86
87/**
88 * DHCP Relay Agent Application Component.
89 */
90@Component(immediate = true)
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +053091public class
92DhcpL2Relay {
Amit Ghosh47243cb2017-07-26 05:08:53 +010093
94 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
95 private final Logger log = LoggerFactory.getLogger(getClass());
96 private final InternalConfigListener cfgListener =
97 new InternalConfigListener();
98
99 private final Set<ConfigFactory> factories = ImmutableSet.of(
100 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
101 DhcpL2RelayConfig.class,
102 "dhcpl2relay") {
103 @Override
104 public DhcpL2RelayConfig createConfig() {
105 return new DhcpL2RelayConfig();
106 }
107 }
108 );
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected NetworkConfigRegistry cfgService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected CoreService coreService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected PacketService packetService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected HostService hostService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected ComponentConfigService componentConfigService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected SubscriberAndDeviceInformationService subsService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected DeviceService deviceService;
130
Amit Ghosh8951f042017-08-10 13:48:10 +0100131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected MastershipService mastershipService;
133
Amit Ghosh47243cb2017-07-26 05:08:53 +0100134 @Property(name = "option82", boolValue = true,
135 label = "Add option 82 to relayed packets")
136 protected boolean option82 = true;
137
Amit Ghosha17354e2017-08-23 12:56:04 +0100138 @Property(name = "enableDhcpBroadcastReplies", boolValue = false,
139 label = "Add option 82 to relayed packets")
140 protected boolean enableDhcpBroadcastReplies = false;
141
Amit Ghosh47243cb2017-07-26 05:08:53 +0100142 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
143 new DhcpRelayPacketProcessor();
144
Amit Ghosh8951f042017-08-10 13:48:10 +0100145 private InnerMastershipListener changeListener = new InnerMastershipListener();
146 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100147
Amit Ghosh8951f042017-08-10 13:48:10 +0100148 // connect points to the DHCP server
149 Set<ConnectPoint> dhcpConnectPoints;
150 private AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100151 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
152 private ApplicationId appId;
153
Amit Ghosha17354e2017-08-23 12:56:04 +0100154 static Map<String, DhcpAllocationInfo> allocationMap = Maps.newConcurrentMap();
155
Amit Ghosh47243cb2017-07-26 05:08:53 +0100156 @Activate
157 protected void activate(ComponentContext context) {
158 //start the dhcp relay agent
159 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
160 componentConfigService.registerProperties(getClass());
161
162 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100163 mastershipService.addListener(changeListener);
164 deviceService.addListener(deviceListener);
165
Amit Ghosh47243cb2017-07-26 05:08:53 +0100166 factories.forEach(cfgService::registerConfigFactory);
167 //update the dhcp server configuration.
168 updateConfig();
169 //add the packet services.
170 packetService.addProcessor(dhcpRelayPacketProcessor,
171 PacketProcessor.director(0));
172 requestDhcpPackets();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000173 if (context != null) {
174 modified(context);
175 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100176
177 log.info("DHCP-L2-RELAY Started");
178 }
179
180 @Deactivate
181 protected void deactivate() {
182 cfgService.removeListener(cfgListener);
183 factories.forEach(cfgService::unregisterConfigFactory);
184 packetService.removeProcessor(dhcpRelayPacketProcessor);
185 cancelDhcpPackets();
186
187 componentConfigService.unregisterProperties(getClass(), false);
Deepa Vaddireddy77a6ac72017-09-20 20:36:52 +0530188 deviceService.removeListener(deviceListener);
189 mastershipService.removeListener(changeListener);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100190 log.info("DHCP-L2-RELAY Stopped");
191 }
192
193 @Modified
194 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000195
Amit Ghosh47243cb2017-07-26 05:08:53 +0100196 Dictionary<?, ?> properties = context.getProperties();
197
198 Boolean o = Tools.isPropertyEnabled(properties, "option82");
199 if (o != null) {
200 option82 = o;
201 }
202 }
203
204 /**
205 * Checks if this app has been configured.
206 *
207 * @return true if all information we need have been initialized
208 */
209 private boolean configured() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100210 return dhcpServerConnectPoint.get() != null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100211 }
212
Amit Ghosh8951f042017-08-10 13:48:10 +0100213 /**
214 * Selects a connect point through an available device for which it is the master.
215 */
216 private void selectServerConnectPoint() {
217 synchronized (this) {
218 dhcpServerConnectPoint.set(null);
219 if (dhcpConnectPoints != null) {
220 // find a connect point through a device for which we are master
221 for (ConnectPoint cp: dhcpConnectPoints) {
222 if (mastershipService.isLocalMaster(cp.deviceId())) {
223 if (deviceService.isAvailable(cp.deviceId())) {
224 dhcpServerConnectPoint.set(cp);
225 }
226 log.info("DHCP connectPoint selected is {}", cp);
227 break;
228 }
229 }
230 }
231
232 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
233
234 if (dhcpServerConnectPoint.get() == null) {
235 log.error("Master of none, can't relay DHCP Message to server");
236 }
237 }
238 }
239
240 /**
241 * Updates the network configuration.
242 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100243 private void updateConfig() {
244 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
245 if (cfg == null) {
246 log.warn("Dhcp Server info not available");
247 return;
248 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100249
250 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
251
252 selectServerConnectPoint();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100253
Amit Ghosh47243cb2017-07-26 05:08:53 +0100254 log.info("dhcp server connect point: " + dhcpServerConnectPoint);
255 }
256
257 /**
258 * Request DHCP packet in via PacketService.
259 */
260 private void requestDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100261 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
262 .matchEthType(Ethernet.TYPE_IPV4)
263 .matchIPProtocol(IPv4.PROTOCOL_UDP)
264 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
265 packetService.requestPackets(selectorServer.build(),
266 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100267
Amit Ghosh8951f042017-08-10 13:48:10 +0100268 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
269 .matchEthType(Ethernet.TYPE_IPV4)
270 .matchIPProtocol(IPv4.PROTOCOL_UDP)
271 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
272 packetService.requestPackets(selectorClient.build(),
273 PacketPriority.CONTROL, appId);
274
Amit Ghosh47243cb2017-07-26 05:08:53 +0100275 }
276
277 /**
278 * Cancel requested DHCP packets in via packet service.
279 */
280 private void cancelDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100281 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
282 .matchEthType(Ethernet.TYPE_IPV4)
283 .matchIPProtocol(IPv4.PROTOCOL_UDP)
284 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
285 packetService.cancelPackets(selectorServer.build(),
286 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100287
Amit Ghosh8951f042017-08-10 13:48:10 +0100288 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
289 .matchEthType(Ethernet.TYPE_IPV4)
290 .matchIPProtocol(IPv4.PROTOCOL_UDP)
291 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
292 packetService.cancelPackets(selectorClient.build(),
293 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100294 }
295
Amit Ghosha17354e2017-08-23 12:56:04 +0100296 public static Map<String, DhcpAllocationInfo> allocationMap() {
297 return allocationMap;
298 }
299
Amit Ghosh47243cb2017-07-26 05:08:53 +0100300 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
301 String serialNo = deviceService.getDevice(context.inPacket().
302 receivedFrom().deviceId()).serialNumber();
303
304 return subsService.get(serialNo);
305 }
306
307 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
308 String serialNo = deviceService.getDevice(cp.deviceId()).
309 serialNumber();
310
311 return subsService.get(serialNo);
312 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100313
314 private MacAddress relayAgentMacAddress(PacketContext context) {
315
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000316 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100317 if (device == null) {
318 log.warn("Device not found for {}", context.inPacket().
319 receivedFrom());
320 return null;
321 }
322
323 return device.hardwareIdentifier();
324 }
325
326 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100327 return nasPortId(context.inPacket().receivedFrom());
328 }
329
330 private String nasPortId(ConnectPoint cp) {
331 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100332 return p.annotations().value(AnnotationKeys.PORT_NAME);
333 }
334
335 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100336 return subsService.get(nasPortId(context));
337 }
338
339 private VlanId cTag(PacketContext context) {
340 SubscriberAndDeviceInformation sub = getSubscriber(context);
341 if (sub == null) {
342 log.warn("Subscriber info not found for {}", context.inPacket().
343 receivedFrom());
344 return VlanId.NONE;
345 }
346 return sub.cTag();
347 }
348
Amit Ghosh8951f042017-08-10 13:48:10 +0100349 private VlanId cTag(ConnectPoint cp) {
350 String portId = nasPortId(cp);
351 SubscriberAndDeviceInformation sub = subsService.get(portId);
352 if (sub == null) {
353 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
354 return VlanId.NONE;
355 }
356 return sub.cTag();
357 }
358
359 private VlanId sTag(ConnectPoint cp) {
360 String portId = nasPortId(cp);
361 SubscriberAndDeviceInformation sub = subsService.get(portId);
362 if (sub == null) {
363 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
364 return VlanId.NONE;
365 }
366 return sub.sTag();
367 }
368
Amit Ghosh47243cb2017-07-26 05:08:53 +0100369 private VlanId sTag(PacketContext context) {
370 SubscriberAndDeviceInformation sub = getSubscriber(context);
371 if (sub == null) {
372 log.warn("Subscriber info not found for {}", context.inPacket().
373 receivedFrom());
374 return VlanId.NONE;
375 }
376 return sub.sTag();
377 }
378
379 private class DhcpRelayPacketProcessor implements PacketProcessor {
380
381 @Override
382 public void process(PacketContext context) {
383 if (!configured()) {
384 log.warn("Missing DHCP relay config. Abort packet processing");
385 return;
386 }
387
388 // process the packet and get the payload
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530389 Ethernet packet = context.inPacket().parsed();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000390
Amit Ghosh47243cb2017-07-26 05:08:53 +0100391 if (packet == null) {
392 log.warn("Packet is null");
393 return;
394 }
395
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000396 log.debug("Got a packet ", packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100397
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530398 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100399 IPv4 ipv4Packet = (IPv4) packet.getPayload();
400
401 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
402 UDP udpPacket = (UDP) ipv4Packet.getPayload();
403 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000404 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100405 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
406 //This packet is dhcp.
407 processDhcpPacket(context, packet, dhcpPayload);
408 }
409 }
410 }
411 }
412
413 //forward the packet to ConnectPoint where the DHCP server is attached.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530414 private void forwardPacket(Ethernet packet) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100415
Amit Ghosh8951f042017-08-10 13:48:10 +0100416 if (dhcpServerConnectPoint.get() != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100417 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh8951f042017-08-10 13:48:10 +0100418 .setOutput(dhcpServerConnectPoint.get().port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100419 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh8951f042017-08-10 13:48:10 +0100420 dhcpServerConnectPoint.get().deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100421 ByteBuffer.wrap(packet.serialize()));
422 if (log.isTraceEnabled()) {
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530423 log.trace("Relaying packet to dhcp server {} at {}",
424 packet, dhcpServerConnectPoint.get());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100425 }
426 packetService.emit(o);
427 } else {
428 log.warn("No dhcp server connect point");
429 }
430 }
431
Amit Ghosha17354e2017-08-23 12:56:04 +0100432 // get the type of the DHCP packet
433 private DHCPPacketType getDhcpPacketType(DHCP dhcpPayload) {
434
435 for (DHCPOption option : dhcpPayload.getOptions()) {
436 if (option.getCode() == OptionCode_MessageType.getValue()) {
437 byte[] data = option.getData();
438 return DHCPPacketType.getType(data[0]);
439 }
440 }
441 return null;
442 }
443
Amit Ghosh47243cb2017-07-26 05:08:53 +0100444 //process the dhcp packet before sending to server
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530445 private void processDhcpPacket(PacketContext context, Ethernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100446 DHCP dhcpPayload) {
447 if (dhcpPayload == null) {
448 log.warn("DHCP payload is null");
449 return;
450 }
451
Amit Ghosha17354e2017-08-23 12:56:04 +0100452 DHCPPacketType incomingPacketType = getDhcpPacketType(dhcpPayload);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000453
Amit Ghosh47243cb2017-07-26 05:08:53 +0100454 log.info("Received DHCP Packet of type {}", incomingPacketType);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100455
456 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000457 case DHCPDISCOVER:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530458 Ethernet ethernetPacketDiscover =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000459 processDhcpPacketFromClient(context, packet);
460 if (ethernetPacketDiscover != null) {
461 forwardPacket(ethernetPacketDiscover);
462 }
463 break;
464 case DHCPOFFER:
465 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530466 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000467 if (ethernetPacketOffer != null) {
468 sendReply(ethernetPacketOffer, dhcpPayload);
469 }
470 break;
471 case DHCPREQUEST:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530472 Ethernet ethernetPacketRequest =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000473 processDhcpPacketFromClient(context, packet);
474 if (ethernetPacketRequest != null) {
475 forwardPacket(ethernetPacketRequest);
476 }
477 break;
478 case DHCPACK:
479 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530480 Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000481 if (ethernetPacketAck != null) {
482 sendReply(ethernetPacketAck, dhcpPayload);
483 }
484 break;
485 default:
486 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100487 }
488 }
489
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530490 private Ethernet processDhcpPacketFromClient(PacketContext context,
491 Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100492
493 MacAddress relayAgentMac = relayAgentMacAddress(context);
494 if (relayAgentMac == null) {
495 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100496 return null;
497 }
498
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530499 Ethernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100500
501 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
502 UDP udpPacket = (UDP) ipv4Packet.getPayload();
503 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
504
Amit Ghosha17354e2017-08-23 12:56:04 +0100505 if (enableDhcpBroadcastReplies) {
506 // We want the reply to come back as a L2 broadcast
507 dhcpPacket.setFlags((short) 0x8000);
508 }
509
510 // remove from the allocation map (used for display) as it's is
511 // requesting a fresh allocation
512 if (getDhcpPacketType(dhcpPacket) == DHCPPacketType.DHCPREQUEST) {
513
514 String portId = nasPortId(context.inPacket().receivedFrom());
515 SubscriberAndDeviceInformation sub = subsService.get(portId);
516 if (sub != null) {
517 allocationMap.remove(sub.nasPortId());
518 }
519 } // end allocation for display
520
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000521 if (option82) {
522 SubscriberAndDeviceInformation entry = getSubscriber(context);
523 if (entry == null) {
524 log.info("Dropping packet as subscriber entry is not available");
525 return null;
526 }
527
528 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
529 udpPacket.setPayload(dhcpPacketWithOption82);
530 }
531
532 ipv4Packet.setPayload(udpPacket);
533 etherReply.setPayload(ipv4Packet);
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530534 etherReply.setSourceMACAddress(relayAgentMac);
535 etherReply.setDestinationMACAddress(dhcpConnectMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100536
Amit Ghosh8951f042017-08-10 13:48:10 +0100537 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100538 etherReply.setVlanID(cTag(context).toShort());
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530539 etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
540 etherReply.setQinQVID(sTag(context).toShort());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100541
Amit Ghosh8951f042017-08-10 13:48:10 +0100542 log.info("Finished processing packet -- sending packet {}", etherReply);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100543 return etherReply;
544 }
545
546 //build the DHCP offer/ack with proper client port.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530547 private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100548 // get dhcp header.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530549 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100550 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
551 UDP udpPacket = (UDP) ipv4Packet.getPayload();
552 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
553
Amit Ghosh47243cb2017-07-26 05:08:53 +0100554 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100555 ConnectPoint subsCp = getConnectPointOfClient(dstMac);
556 // if it's an ACK packet store the information for display purpose
557 if (getDhcpPacketType(dhcpPayload) == DHCPPacketType.DHCPACK) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100558
Amit Ghosha17354e2017-08-23 12:56:04 +0100559 String portId = nasPortId(subsCp);
560 SubscriberAndDeviceInformation sub = subsService.get(portId);
561 if (sub != null) {
562 List<DHCPOption> options = dhcpPayload.getOptions();
563 List<DHCPOption> circuitIds = options.stream()
564 .filter(option -> option.getCode() == DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
565 .collect(Collectors.toList());
566
567 String circuitId = "None";
568 if (circuitIds.size() == 1) {
569 circuitId = circuitIds.get(0).getData().toString();
570 }
571
572 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
573
574 //storeDHCPAllocationInfo
575 DhcpAllocationInfo info = new DhcpAllocationInfo(circuitId, dstMac, ip);
576
577 allocationMap.put(sub.nasPortId(), info);
578 }
579 } // end storing of info
Amit Ghosh47243cb2017-07-26 05:08:53 +0100580
581 // we leave the srcMac from the original packet
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530582 etherReply.setDestinationMACAddress(dstMac);
583 etherReply.setQinQVID(sTag(subsCp).toShort());
Amit Ghosh8951f042017-08-10 13:48:10 +0100584 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
585 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100586
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000587 if (option82) {
588 udpPacket.setPayload(removeOption82(dhcpPayload));
589 } else {
590 udpPacket.setPayload(dhcpPayload);
591 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100592 ipv4Packet.setPayload(udpPacket);
593 etherReply.setPayload(ipv4Packet);
594
595 log.info("Finished processing packet");
596 return etherReply;
597 }
598
Amit Ghosha17354e2017-08-23 12:56:04 +0100599 /*
600 * Get ConnectPoint of the Client based on it's MAC address
601 */
602 private ConnectPoint getConnectPointOfClient(MacAddress dstMac) {
603 Set<Host> hosts = hostService.getHostsByMac(dstMac);
604 if (hosts == null || hosts.isEmpty()) {
605 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530606 + "relay for dhcp packet from server",
Amit Ghosha17354e2017-08-23 12:56:04 +0100607 dstMac);
608 return null;
609 }
610 for (Host h : hosts) {
611 // if more than one,
612 // find the connect point which has an valid entry in SADIS
613 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
614 h.location().port());
615
616 if (sTag(cp) != VlanId.NONE) {
617 return cp;
618 }
619 }
620
621 return null;
622 }
623
Amit Ghosh47243cb2017-07-26 05:08:53 +0100624 //send the response to the requester host.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530625 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100626 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100627 ConnectPoint subCp = getConnectPointOfClient(descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100628
629 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +0100630 if (subCp != null) {
631 log.info("Sending DHCP packet to cp: {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100632 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +0100633 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100634 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +0100635 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100636 if (log.isTraceEnabled()) {
637 log.trace("Relaying packet to dhcp client {}", ethPacket);
638 }
639 packetService.emit(o);
Amit Ghosha17354e2017-08-23 12:56:04 +0100640 log.info("DHCP Packet sent to {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100641 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000642 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100643 }
644 }
645 }
646
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000647 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
648 log.debug("option82data {} ", entry);
649
650 List<DHCPOption> options = Lists.newArrayList(dhcpPacket.getOptions());
651 DhcpOption82 option82 = new DhcpOption82();
652 option82.setAgentCircuitId(entry.circuitId());
653 option82.setAgentRemoteId(entry.remoteId());
654 DHCPOption option = new DHCPOption()
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530655 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
656 .setData(option82.toByteArray())
657 .setLength(option82.length());
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000658
659 options.add(options.size() - 1, option);
660 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +0100661
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000662 return dhcpPacket;
663
664 }
665
666 private DHCP removeOption82(DHCP dhcpPacket) {
667 List<DHCPOption> options = dhcpPacket.getOptions();
668 List<DHCPOption> newoptions = options.stream()
669 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
670 .collect(Collectors.toList());
671
672 return dhcpPacket.setOptions(newoptions);
673 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100674 /**
675 * Listener for network config events.
676 */
677 private class InternalConfigListener implements NetworkConfigListener {
678
679 @Override
680 public void event(NetworkConfigEvent event) {
681
682 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
683 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
684 event.configClass().equals(DhcpL2RelayConfig.class)) {
685 updateConfig();
686 log.info("Reconfigured");
687 }
688 }
689 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000690
Amit Ghosh8951f042017-08-10 13:48:10 +0100691 /**
692 * Handles Mastership changes for the devices which connect
693 * to the DHCP server.
694 */
695 private class InnerMastershipListener implements MastershipListener {
696 @Override
697 public void event(MastershipEvent event) {
698 if (dhcpServerConnectPoint.get() != null &&
699 dhcpServerConnectPoint.get().deviceId().
700 equals(event.subject())) {
701 log.trace("Mastership Event recevived for {}", event.subject());
702 // mastership of the device for our connect point has changed
703 // reselect
704 selectServerConnectPoint();
705 }
706 }
707 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000708
Amit Ghosh8951f042017-08-10 13:48:10 +0100709 /**
710 * Handles Device status change for the devices which connect
711 * to the DHCP server.
712 */
713 private class InnerDeviceListener implements DeviceListener {
714 @Override
715 public void event(DeviceEvent event) {
716 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
717 if (dhcpServerConnectPoint.get() == null) {
718 switch (event.type()) {
719 case DEVICE_ADDED:
720 case DEVICE_AVAILABILITY_CHANGED:
721 // some device is available check if we can get one
722 selectServerConnectPoint();
723 break;
724 default:
725 break;
726 }
727 return;
728 }
729 if (dhcpServerConnectPoint.get().deviceId().
730 equals(event.subject().id())) {
731 switch (event.type()) {
732 case DEVICE_AVAILABILITY_CHANGED:
733 case DEVICE_REMOVED:
734 case DEVICE_SUSPENDED:
735 // state of our device has changed, check if we need
736 // to re-select
737 selectServerConnectPoint();
738 break;
739 default:
740 break;
741 }
742 }
743 }
744 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100745}