blob: 209a5da3e51de91b9f1797fdad117eabc45edeba [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);
188
189 log.info("DHCP-L2-RELAY Stopped");
190 }
191
192 @Modified
193 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000194
Amit Ghosh47243cb2017-07-26 05:08:53 +0100195 Dictionary<?, ?> properties = context.getProperties();
196
197 Boolean o = Tools.isPropertyEnabled(properties, "option82");
198 if (o != null) {
199 option82 = o;
200 }
201 }
202
203 /**
204 * Checks if this app has been configured.
205 *
206 * @return true if all information we need have been initialized
207 */
208 private boolean configured() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100209 return dhcpServerConnectPoint.get() != null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100210 }
211
Amit Ghosh8951f042017-08-10 13:48:10 +0100212 /**
213 * Selects a connect point through an available device for which it is the master.
214 */
215 private void selectServerConnectPoint() {
216 synchronized (this) {
217 dhcpServerConnectPoint.set(null);
218 if (dhcpConnectPoints != null) {
219 // find a connect point through a device for which we are master
220 for (ConnectPoint cp: dhcpConnectPoints) {
221 if (mastershipService.isLocalMaster(cp.deviceId())) {
222 if (deviceService.isAvailable(cp.deviceId())) {
223 dhcpServerConnectPoint.set(cp);
224 }
225 log.info("DHCP connectPoint selected is {}", cp);
226 break;
227 }
228 }
229 }
230
231 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
232
233 if (dhcpServerConnectPoint.get() == null) {
234 log.error("Master of none, can't relay DHCP Message to server");
235 }
236 }
237 }
238
239 /**
240 * Updates the network configuration.
241 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100242 private void updateConfig() {
243 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
244 if (cfg == null) {
245 log.warn("Dhcp Server info not available");
246 return;
247 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100248
249 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
250
251 selectServerConnectPoint();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100252
Amit Ghosh47243cb2017-07-26 05:08:53 +0100253 log.info("dhcp server connect point: " + dhcpServerConnectPoint);
254 }
255
256 /**
257 * Request DHCP packet in via PacketService.
258 */
259 private void requestDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100260 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
261 .matchEthType(Ethernet.TYPE_IPV4)
262 .matchIPProtocol(IPv4.PROTOCOL_UDP)
263 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
264 packetService.requestPackets(selectorServer.build(),
265 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100266
Amit Ghosh8951f042017-08-10 13:48:10 +0100267 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
268 .matchEthType(Ethernet.TYPE_IPV4)
269 .matchIPProtocol(IPv4.PROTOCOL_UDP)
270 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
271 packetService.requestPackets(selectorClient.build(),
272 PacketPriority.CONTROL, appId);
273
Amit Ghosh47243cb2017-07-26 05:08:53 +0100274 }
275
276 /**
277 * Cancel requested DHCP packets in via packet service.
278 */
279 private void cancelDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100280 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
281 .matchEthType(Ethernet.TYPE_IPV4)
282 .matchIPProtocol(IPv4.PROTOCOL_UDP)
283 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
284 packetService.cancelPackets(selectorServer.build(),
285 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100286
Amit Ghosh8951f042017-08-10 13:48:10 +0100287 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
288 .matchEthType(Ethernet.TYPE_IPV4)
289 .matchIPProtocol(IPv4.PROTOCOL_UDP)
290 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
291 packetService.cancelPackets(selectorClient.build(),
292 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100293 }
294
Amit Ghosha17354e2017-08-23 12:56:04 +0100295 public static Map<String, DhcpAllocationInfo> allocationMap() {
296 return allocationMap;
297 }
298
Amit Ghosh47243cb2017-07-26 05:08:53 +0100299 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
300 String serialNo = deviceService.getDevice(context.inPacket().
301 receivedFrom().deviceId()).serialNumber();
302
303 return subsService.get(serialNo);
304 }
305
306 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
307 String serialNo = deviceService.getDevice(cp.deviceId()).
308 serialNumber();
309
310 return subsService.get(serialNo);
311 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100312
313 private MacAddress relayAgentMacAddress(PacketContext context) {
314
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000315 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100316 if (device == null) {
317 log.warn("Device not found for {}", context.inPacket().
318 receivedFrom());
319 return null;
320 }
321
322 return device.hardwareIdentifier();
323 }
324
325 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100326 return nasPortId(context.inPacket().receivedFrom());
327 }
328
329 private String nasPortId(ConnectPoint cp) {
330 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100331 return p.annotations().value(AnnotationKeys.PORT_NAME);
332 }
333
334 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100335 return subsService.get(nasPortId(context));
336 }
337
338 private VlanId cTag(PacketContext context) {
339 SubscriberAndDeviceInformation sub = getSubscriber(context);
340 if (sub == null) {
341 log.warn("Subscriber info not found for {}", context.inPacket().
342 receivedFrom());
343 return VlanId.NONE;
344 }
345 return sub.cTag();
346 }
347
Amit Ghosh8951f042017-08-10 13:48:10 +0100348 private VlanId cTag(ConnectPoint cp) {
349 String portId = nasPortId(cp);
350 SubscriberAndDeviceInformation sub = subsService.get(portId);
351 if (sub == null) {
352 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
353 return VlanId.NONE;
354 }
355 return sub.cTag();
356 }
357
358 private VlanId sTag(ConnectPoint cp) {
359 String portId = nasPortId(cp);
360 SubscriberAndDeviceInformation sub = subsService.get(portId);
361 if (sub == null) {
362 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
363 return VlanId.NONE;
364 }
365 return sub.sTag();
366 }
367
Amit Ghosh47243cb2017-07-26 05:08:53 +0100368 private VlanId sTag(PacketContext context) {
369 SubscriberAndDeviceInformation sub = getSubscriber(context);
370 if (sub == null) {
371 log.warn("Subscriber info not found for {}", context.inPacket().
372 receivedFrom());
373 return VlanId.NONE;
374 }
375 return sub.sTag();
376 }
377
378 private class DhcpRelayPacketProcessor implements PacketProcessor {
379
380 @Override
381 public void process(PacketContext context) {
382 if (!configured()) {
383 log.warn("Missing DHCP relay config. Abort packet processing");
384 return;
385 }
386
387 // process the packet and get the payload
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530388 Ethernet packet = context.inPacket().parsed();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000389
Amit Ghosh47243cb2017-07-26 05:08:53 +0100390 if (packet == null) {
391 log.warn("Packet is null");
392 return;
393 }
394
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000395 log.debug("Got a packet ", packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100396
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530397 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100398 IPv4 ipv4Packet = (IPv4) packet.getPayload();
399
400 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
401 UDP udpPacket = (UDP) ipv4Packet.getPayload();
402 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000403 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100404 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
405 //This packet is dhcp.
406 processDhcpPacket(context, packet, dhcpPayload);
407 }
408 }
409 }
410 }
411
412 //forward the packet to ConnectPoint where the DHCP server is attached.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530413 private void forwardPacket(Ethernet packet) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100414
Amit Ghosh8951f042017-08-10 13:48:10 +0100415 if (dhcpServerConnectPoint.get() != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100416 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh8951f042017-08-10 13:48:10 +0100417 .setOutput(dhcpServerConnectPoint.get().port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100418 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh8951f042017-08-10 13:48:10 +0100419 dhcpServerConnectPoint.get().deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100420 ByteBuffer.wrap(packet.serialize()));
421 if (log.isTraceEnabled()) {
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530422 log.trace("Relaying packet to dhcp server {} at {}",
423 packet, dhcpServerConnectPoint.get());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100424 }
425 packetService.emit(o);
426 } else {
427 log.warn("No dhcp server connect point");
428 }
429 }
430
Amit Ghosha17354e2017-08-23 12:56:04 +0100431 // get the type of the DHCP packet
432 private DHCPPacketType getDhcpPacketType(DHCP dhcpPayload) {
433
434 for (DHCPOption option : dhcpPayload.getOptions()) {
435 if (option.getCode() == OptionCode_MessageType.getValue()) {
436 byte[] data = option.getData();
437 return DHCPPacketType.getType(data[0]);
438 }
439 }
440 return null;
441 }
442
Amit Ghosh47243cb2017-07-26 05:08:53 +0100443 //process the dhcp packet before sending to server
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530444 private void processDhcpPacket(PacketContext context, Ethernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100445 DHCP dhcpPayload) {
446 if (dhcpPayload == null) {
447 log.warn("DHCP payload is null");
448 return;
449 }
450
Amit Ghosha17354e2017-08-23 12:56:04 +0100451 DHCPPacketType incomingPacketType = getDhcpPacketType(dhcpPayload);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000452
Amit Ghosh47243cb2017-07-26 05:08:53 +0100453 log.info("Received DHCP Packet of type {}", incomingPacketType);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100454
455 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000456 case DHCPDISCOVER:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530457 Ethernet ethernetPacketDiscover =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000458 processDhcpPacketFromClient(context, packet);
459 if (ethernetPacketDiscover != null) {
460 forwardPacket(ethernetPacketDiscover);
461 }
462 break;
463 case DHCPOFFER:
464 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530465 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000466 if (ethernetPacketOffer != null) {
467 sendReply(ethernetPacketOffer, dhcpPayload);
468 }
469 break;
470 case DHCPREQUEST:
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530471 Ethernet ethernetPacketRequest =
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000472 processDhcpPacketFromClient(context, packet);
473 if (ethernetPacketRequest != null) {
474 forwardPacket(ethernetPacketRequest);
475 }
476 break;
477 case DHCPACK:
478 //reply to dhcp client.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530479 Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000480 if (ethernetPacketAck != null) {
481 sendReply(ethernetPacketAck, dhcpPayload);
482 }
483 break;
484 default:
485 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100486 }
487 }
488
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530489 private Ethernet processDhcpPacketFromClient(PacketContext context,
490 Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100491
492 MacAddress relayAgentMac = relayAgentMacAddress(context);
493 if (relayAgentMac == null) {
494 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100495 return null;
496 }
497
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530498 Ethernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100499
500 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
501 UDP udpPacket = (UDP) ipv4Packet.getPayload();
502 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
503
Amit Ghosha17354e2017-08-23 12:56:04 +0100504 if (enableDhcpBroadcastReplies) {
505 // We want the reply to come back as a L2 broadcast
506 dhcpPacket.setFlags((short) 0x8000);
507 }
508
509 // remove from the allocation map (used for display) as it's is
510 // requesting a fresh allocation
511 if (getDhcpPacketType(dhcpPacket) == DHCPPacketType.DHCPREQUEST) {
512
513 String portId = nasPortId(context.inPacket().receivedFrom());
514 SubscriberAndDeviceInformation sub = subsService.get(portId);
515 if (sub != null) {
516 allocationMap.remove(sub.nasPortId());
517 }
518 } // end allocation for display
519
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000520 if (option82) {
521 SubscriberAndDeviceInformation entry = getSubscriber(context);
522 if (entry == null) {
523 log.info("Dropping packet as subscriber entry is not available");
524 return null;
525 }
526
527 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
528 udpPacket.setPayload(dhcpPacketWithOption82);
529 }
530
531 ipv4Packet.setPayload(udpPacket);
532 etherReply.setPayload(ipv4Packet);
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530533 etherReply.setSourceMACAddress(relayAgentMac);
534 etherReply.setDestinationMACAddress(dhcpConnectMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100535
Amit Ghosh8951f042017-08-10 13:48:10 +0100536 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100537 etherReply.setVlanID(cTag(context).toShort());
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530538 etherReply.setQinQTPID(Ethernet.TYPE_VLAN);
539 etherReply.setQinQVID(sTag(context).toShort());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100540
Amit Ghosh8951f042017-08-10 13:48:10 +0100541 log.info("Finished processing packet -- sending packet {}", etherReply);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100542 return etherReply;
543 }
544
545 //build the DHCP offer/ack with proper client port.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530546 private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100547 // get dhcp header.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530548 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100549 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
550 UDP udpPacket = (UDP) ipv4Packet.getPayload();
551 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
552
Amit Ghosh47243cb2017-07-26 05:08:53 +0100553 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100554 ConnectPoint subsCp = getConnectPointOfClient(dstMac);
555 // if it's an ACK packet store the information for display purpose
556 if (getDhcpPacketType(dhcpPayload) == DHCPPacketType.DHCPACK) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100557
Amit Ghosha17354e2017-08-23 12:56:04 +0100558 String portId = nasPortId(subsCp);
559 SubscriberAndDeviceInformation sub = subsService.get(portId);
560 if (sub != null) {
561 List<DHCPOption> options = dhcpPayload.getOptions();
562 List<DHCPOption> circuitIds = options.stream()
563 .filter(option -> option.getCode() == DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
564 .collect(Collectors.toList());
565
566 String circuitId = "None";
567 if (circuitIds.size() == 1) {
568 circuitId = circuitIds.get(0).getData().toString();
569 }
570
571 IpAddress ip = IpAddress.valueOf(dhcpPayload.getYourIPAddress());
572
573 //storeDHCPAllocationInfo
574 DhcpAllocationInfo info = new DhcpAllocationInfo(circuitId, dstMac, ip);
575
576 allocationMap.put(sub.nasPortId(), info);
577 }
578 } // end storing of info
Amit Ghosh47243cb2017-07-26 05:08:53 +0100579
580 // we leave the srcMac from the original packet
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530581 etherReply.setDestinationMACAddress(dstMac);
582 etherReply.setQinQVID(sTag(subsCp).toShort());
Amit Ghosh8951f042017-08-10 13:48:10 +0100583 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
584 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100585
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000586 if (option82) {
587 udpPacket.setPayload(removeOption82(dhcpPayload));
588 } else {
589 udpPacket.setPayload(dhcpPayload);
590 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100591 ipv4Packet.setPayload(udpPacket);
592 etherReply.setPayload(ipv4Packet);
593
594 log.info("Finished processing packet");
595 return etherReply;
596 }
597
Amit Ghosha17354e2017-08-23 12:56:04 +0100598 /*
599 * Get ConnectPoint of the Client based on it's MAC address
600 */
601 private ConnectPoint getConnectPointOfClient(MacAddress dstMac) {
602 Set<Host> hosts = hostService.getHostsByMac(dstMac);
603 if (hosts == null || hosts.isEmpty()) {
604 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530605 + "relay for dhcp packet from server",
Amit Ghosha17354e2017-08-23 12:56:04 +0100606 dstMac);
607 return null;
608 }
609 for (Host h : hosts) {
610 // if more than one,
611 // find the connect point which has an valid entry in SADIS
612 ConnectPoint cp = new ConnectPoint(h.location().deviceId(),
613 h.location().port());
614
615 if (sTag(cp) != VlanId.NONE) {
616 return cp;
617 }
618 }
619
620 return null;
621 }
622
Amit Ghosh47243cb2017-07-26 05:08:53 +0100623 //send the response to the requester host.
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530624 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100625 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
Amit Ghosha17354e2017-08-23 12:56:04 +0100626 ConnectPoint subCp = getConnectPointOfClient(descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100627
628 // Send packet out to requester if the host information is available
Amit Ghosha17354e2017-08-23 12:56:04 +0100629 if (subCp != null) {
630 log.info("Sending DHCP packet to cp: {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100631 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosha17354e2017-08-23 12:56:04 +0100632 .setOutput(subCp.port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100633 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosha17354e2017-08-23 12:56:04 +0100634 subCp.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100635 if (log.isTraceEnabled()) {
636 log.trace("Relaying packet to dhcp client {}", ethPacket);
637 }
638 packetService.emit(o);
Amit Ghosha17354e2017-08-23 12:56:04 +0100639 log.info("DHCP Packet sent to {}", subCp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100640 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000641 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100642 }
643 }
644 }
645
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000646 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
647 log.debug("option82data {} ", entry);
648
649 List<DHCPOption> options = Lists.newArrayList(dhcpPacket.getOptions());
650 DhcpOption82 option82 = new DhcpOption82();
651 option82.setAgentCircuitId(entry.circuitId());
652 option82.setAgentRemoteId(entry.remoteId());
653 DHCPOption option = new DHCPOption()
Deepa Vaddireddy5f278d62017-08-30 05:59:39 +0530654 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
655 .setData(option82.toByteArray())
656 .setLength(option82.length());
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000657
658 options.add(options.size() - 1, option);
659 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +0100660
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000661 return dhcpPacket;
662
663 }
664
665 private DHCP removeOption82(DHCP dhcpPacket) {
666 List<DHCPOption> options = dhcpPacket.getOptions();
667 List<DHCPOption> newoptions = options.stream()
668 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
669 .collect(Collectors.toList());
670
671 return dhcpPacket.setOptions(newoptions);
672 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100673 /**
674 * Listener for network config events.
675 */
676 private class InternalConfigListener implements NetworkConfigListener {
677
678 @Override
679 public void event(NetworkConfigEvent event) {
680
681 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
682 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
683 event.configClass().equals(DhcpL2RelayConfig.class)) {
684 updateConfig();
685 log.info("Reconfigured");
686 }
687 }
688 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000689
Amit Ghosh8951f042017-08-10 13:48:10 +0100690 /**
691 * Handles Mastership changes for the devices which connect
692 * to the DHCP server.
693 */
694 private class InnerMastershipListener implements MastershipListener {
695 @Override
696 public void event(MastershipEvent event) {
697 if (dhcpServerConnectPoint.get() != null &&
698 dhcpServerConnectPoint.get().deviceId().
699 equals(event.subject())) {
700 log.trace("Mastership Event recevived for {}", event.subject());
701 // mastership of the device for our connect point has changed
702 // reselect
703 selectServerConnectPoint();
704 }
705 }
706 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000707
Amit Ghosh8951f042017-08-10 13:48:10 +0100708 /**
709 * Handles Device status change for the devices which connect
710 * to the DHCP server.
711 */
712 private class InnerDeviceListener implements DeviceListener {
713 @Override
714 public void event(DeviceEvent event) {
715 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
716 if (dhcpServerConnectPoint.get() == null) {
717 switch (event.type()) {
718 case DEVICE_ADDED:
719 case DEVICE_AVAILABILITY_CHANGED:
720 // some device is available check if we can get one
721 selectServerConnectPoint();
722 break;
723 default:
724 break;
725 }
726 return;
727 }
728 if (dhcpServerConnectPoint.get().deviceId().
729 equals(event.subject().id())) {
730 switch (event.type()) {
731 case DEVICE_AVAILABILITY_CHANGED:
732 case DEVICE_REMOVED:
733 case DEVICE_SUSPENDED:
734 // state of our device has changed, check if we need
735 // to re-select
736 selectServerConnectPoint();
737 break;
738 default:
739 break;
740 }
741 }
742 }
743 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100744}