blob: 85bf31c51173aac8f17567e7a4e40d55595e018b [file] [log] [blame]
gauravf0884562016-06-17 02:47:13 +05301/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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.onosproject.dhcprelay;
17
gaurav4af95fb2016-07-07 01:48:44 +053018import java.nio.ByteBuffer;
gaurav38351de2016-07-27 04:44:33 +053019import java.util.Objects;
gauravf0884562016-06-17 02:47:13 +053020import java.util.Set;
gauravf0884562016-06-17 02:47:13 +053021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
gaurav38351de2016-07-27 04:44:33 +053026import org.onlab.packet.ARP;
gauravf0884562016-06-17 02:47:13 +053027import org.onlab.packet.DHCP;
gaurav4af95fb2016-07-07 01:48:44 +053028import org.onlab.packet.DHCPOption;
29import org.onlab.packet.DHCPPacketType;
gauravf0884562016-06-17 02:47:13 +053030import org.onlab.packet.Ethernet;
31import org.onlab.packet.IPv4;
gaurav38351de2016-07-27 04:44:33 +053032import org.onlab.packet.Ip4Address;
33import org.onlab.packet.MacAddress;
gauravf0884562016-06-17 02:47:13 +053034import org.onlab.packet.TpPort;
35import org.onlab.packet.UDP;
gaurav4af95fb2016-07-07 01:48:44 +053036import org.onlab.packet.VlanId;
gauravf0884562016-06-17 02:47:13 +053037import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
gaurav38351de2016-07-27 04:44:33 +053039import org.onosproject.incubator.net.intf.Interface;
40import org.onosproject.incubator.net.intf.InterfaceService;
gauravf0884562016-06-17 02:47:13 +053041import org.onosproject.net.ConnectPoint;
42import org.onosproject.net.Host;
43import org.onosproject.net.HostId;
44import org.onosproject.net.config.ConfigFactory;
45import org.onosproject.net.config.NetworkConfigEvent;
46import org.onosproject.net.config.NetworkConfigListener;
47import org.onosproject.net.config.NetworkConfigRegistry;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.TrafficSelector;
51import org.onosproject.net.flow.TrafficTreatment;
52import org.onosproject.net.host.HostService;
53import org.onosproject.net.packet.DefaultOutboundPacket;
54import org.onosproject.net.packet.OutboundPacket;
55import org.onosproject.net.packet.PacketContext;
56import org.onosproject.net.packet.PacketPriority;
57import org.onosproject.net.packet.PacketProcessor;
58import org.onosproject.net.packet.PacketService;
59import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
62import com.google.common.collect.ImmutableSet;
gaurav38351de2016-07-27 04:44:33 +053063
gauravf0884562016-06-17 02:47:13 +053064import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
gaurav4af95fb2016-07-07 01:48:44 +053065import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
gaurav38351de2016-07-27 04:44:33 +053066import static org.onlab.packet.MacAddress.valueOf;
gauravf0884562016-06-17 02:47:13 +053067/**
68 * DHCP Relay Agent Application Component.
69 */
70@Component(immediate = true)
71public class DhcpRelay {
72
73 public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
74 private final Logger log = LoggerFactory.getLogger(getClass());
75 private final InternalConfigListener cfgListener = new InternalConfigListener();
gaurav38351de2016-07-27 04:44:33 +053076 private static Ip4Address relayAgentIP = null;
77 private static MacAddress relayAgentMAC = null;
78 private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
gauravf0884562016-06-17 02:47:13 +053079
80 private final Set<ConfigFactory> factories = ImmutableSet.of(
81 new ConfigFactory<ApplicationId, DhcpRelayConfig>(APP_SUBJECT_FACTORY,
82 DhcpRelayConfig.class,
83 "dhcprelay") {
84 @Override
85 public DhcpRelayConfig createConfig() {
86 return new DhcpRelayConfig();
87 }
88 }
89 );
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected NetworkConfigRegistry cfgService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected CoreService coreService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected PacketService packetService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected HostService hostService;
102
gaurav38351de2016-07-27 04:44:33 +0530103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected InterfaceService interfaceService;
105
gauravf0884562016-06-17 02:47:13 +0530106 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
107 private ConnectPoint dhcpServerConnectPoint = null;
gaurav38351de2016-07-27 04:44:33 +0530108 private Ip4Address dhcpServerIp = null;
109 private MacAddress dhcpServerMac = null;
gauravf0884562016-06-17 02:47:13 +0530110 private ApplicationId appId;
111
112 @Activate
113 protected void activate() {
114 //start the dhcp relay agent
115
116 appId = coreService.registerApplication(DHCP_RELAY_APP);
117
118 cfgService.addListener(cfgListener);
119 factories.forEach(cfgService::registerConfigFactory);
120 //update the dhcp server configuration.
121 updateConfig();
122 //add the packet services.
123 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
124 requestPackets();
125 log.info("DHCP-RELAY Started");
126 log.info("started the apps dhcp relay");
127 }
128
129 @Deactivate
130 protected void deactivate() {
131 cfgService.removeListener(cfgListener);
132 factories.forEach(cfgService::unregisterConfigFactory);
133 packetService.removeProcessor(dhcpRelayPacketProcessor);
134 cancelPackets();
135 log.info("DHCP-RELAY Stopped");
136 }
137
138 private void updateConfig() {
139 DhcpRelayConfig cfg = cfgService.getConfig(appId, DhcpRelayConfig.class);
140
141 if (cfg == null) {
142 log.warn("Dhcp Server info not available");
143 return;
144 }
145
146 dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
gaurav38351de2016-07-27 04:44:33 +0530147 dhcpServerIp = cfg.getDhcpServerIp();
148 dhcpServerMac = cfg.getDhcpServermac();
gauravf0884562016-06-17 02:47:13 +0530149 log.info("dhcp server connect points are " + dhcpServerConnectPoint);
gaurav38351de2016-07-27 04:44:33 +0530150 log.info("dhcp server ipaddress " + dhcpServerIp);
151 log.info("dhcp server mac address " + dhcpServerMac);
gauravf0884562016-06-17 02:47:13 +0530152 }
153
154 /**
155 * Request packet in via PacketService.
156 */
157 private void requestPackets() {
158
159 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
160 .matchEthType(Ethernet.TYPE_IPV4)
161 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530162 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530163 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
164
165 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
166 .matchEthType(Ethernet.TYPE_IPV4)
167 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530168 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530169 packetService.requestPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
gaurav38351de2016-07-27 04:44:33 +0530170
171 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
172 .matchEthType(Ethernet.TYPE_ARP);
173 packetService.requestPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
gauravf0884562016-06-17 02:47:13 +0530174 }
175
176 /**
177 * Cancel requested packets in via packet service.
178 */
179 private void cancelPackets() {
180 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
181 .matchEthType(Ethernet.TYPE_IPV4)
182 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530183 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530184 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
185
186 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
187 .matchEthType(Ethernet.TYPE_IPV4)
188 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530189 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530190 packetService.cancelPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
gaurav38351de2016-07-27 04:44:33 +0530191
192 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
193 .matchEthType(Ethernet.TYPE_ARP);
194 packetService.cancelPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
gauravf0884562016-06-17 02:47:13 +0530195 }
196
197 private class DhcpRelayPacketProcessor implements PacketProcessor {
198
199 @Override
200 public void process(PacketContext context) {
201 // process the packet and get the payload
202 Ethernet packet = context.inPacket().parsed();
203
204 if (packet == null) {
205 return;
206 }
207
208 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
209 IPv4 ipv4Packet = (IPv4) packet.getPayload();
210
211 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
212 UDP udpPacket = (UDP) ipv4Packet.getPayload();
213 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
gaurav38351de2016-07-27 04:44:33 +0530214 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
215 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
gaurav4af95fb2016-07-07 01:48:44 +0530216 //This packet is dhcp.
217 processDhcpPacket(context, dhcpPayload);
gauravf0884562016-06-17 02:47:13 +0530218 }
219 }
gaurav38351de2016-07-27 04:44:33 +0530220 } else if (packet.getEtherType() == Ethernet.TYPE_ARP) {
221 ARP arpPacket = (ARP) packet.getPayload();
222 Set<Interface> serverInterfaces = interfaceService.
223 getInterfacesByPort(context.inPacket().receivedFrom());
224 //ignore the packets if dhcp server interface is not configured on onos.
225 if (serverInterfaces.isEmpty()) {
226 log.warn("server virtual interface not configured");
227 return;
228 }
229 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
230 checkArpRequestFrmDhcpServ(serverInterfaces, arpPacket)) {
231 processArpPacket(context, packet);
232 }
gauravf0884562016-06-17 02:47:13 +0530233 }
234 }
235
gaurav38351de2016-07-27 04:44:33 +0530236 //method to check the arp request is from dhcp server for default-gateway.
237 private boolean checkArpRequestFrmDhcpServ(Set<Interface> serverInterfaces, ARP arpPacket) {
238 if (Objects.equals(serverInterfaces.iterator().next().ipAddressesList().get(0).
239 ipAddress().getIp4Address(),
240 Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
241 return true;
242 }
243 return false;
244 }
245
gauravf0884562016-06-17 02:47:13 +0530246 //forward the packet to ConnectPoint where the DHCP server is attached.
gaurav4af95fb2016-07-07 01:48:44 +0530247 private void forwardPacket(Ethernet packet) {
gauravf0884562016-06-17 02:47:13 +0530248
249 //send Packetout to dhcp server connectpoint.
250 if (dhcpServerConnectPoint != null) {
251 TrafficTreatment t = DefaultTrafficTreatment.builder()
252 .setOutput(dhcpServerConnectPoint.port()).build();
253 OutboundPacket o = new DefaultOutboundPacket(
gaurav4af95fb2016-07-07 01:48:44 +0530254 dhcpServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
gauravf0884562016-06-17 02:47:13 +0530255 packetService.emit(o);
256 }
257 }
258
gaurav38351de2016-07-27 04:44:33 +0530259 /**
260 * Processes the ARP Payload and initiates a reply to the client.
261 *
262 * @param context context of the incoming message
263 * @param packet the ethernet payload
264 */
265 private void processArpPacket(PacketContext context, Ethernet packet) {
266
267 ARP arpPacket = (ARP) packet.getPayload();
268
269 ARP arpReply = (ARP) arpPacket.clone();
270 arpReply.setOpCode(ARP.OP_REPLY);
271
272 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
273 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
274 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
275 arpReply.setSenderHardwareAddress(myMAC.toBytes());
276
277 // Ethernet Frame.
278 Ethernet ethReply = new Ethernet();
279 ethReply.setSourceMACAddress(myMAC);
280 ethReply.setDestinationMACAddress(packet.getSourceMAC());
281 ethReply.setEtherType(Ethernet.TYPE_ARP);
282 ethReply.setVlanID(packet.getVlanID());
283
284 ethReply.setPayload(arpReply);
285 forwardPacket(ethReply);
286 }
287
gaurav4af95fb2016-07-07 01:48:44 +0530288 //process the dhcp packet before sending to server
gauravf0884562016-06-17 02:47:13 +0530289 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Charles Chan1b201232016-12-07 15:39:22 -0800290 ConnectPoint inPort = context.inPacket().receivedFrom();
291 Set<Interface> clientServerInterfaces = interfaceService.getInterfacesByPort(inPort);
gaurav38351de2016-07-27 04:44:33 +0530292 //ignore the packets if dhcp client interface is not configured on onos.
293 if (clientServerInterfaces.isEmpty()) {
Charles Chan1b201232016-12-07 15:39:22 -0800294 log.warn("Virtual interface is not configured on {}", inPort);
gaurav38351de2016-07-27 04:44:33 +0530295 return;
296 }
297
gauravf0884562016-06-17 02:47:13 +0530298 if (dhcpPayload == null) {
299 return;
300 }
gaurav4af95fb2016-07-07 01:48:44 +0530301
gauravf0884562016-06-17 02:47:13 +0530302 Ethernet packet = context.inPacket().parsed();
303 DHCPPacketType incomingPacketType = null;
304 for (DHCPOption option : dhcpPayload.getOptions()) {
305 if (option.getCode() == OptionCode_MessageType.getValue()) {
306 byte[] data = option.getData();
307 incomingPacketType = DHCPPacketType.getType(data[0]);
308 }
309 }
gaurav38351de2016-07-27 04:44:33 +0530310
gauravf0884562016-06-17 02:47:13 +0530311 switch (incomingPacketType) {
312 case DHCPDISCOVER:
gaurav38351de2016-07-27 04:44:33 +0530313 //add the gatewayip as virtual interface ip for server to understand the lease to be assigned
314 //and forward the packet to dhcp server.
315 Ethernet ethernetPacketDiscover = processDhcpPacketFrmClient(context, packet, clientServerInterfaces);
gaurav4af95fb2016-07-07 01:48:44 +0530316 forwardPacket(ethernetPacketDiscover);
317 break;
318 case DHCPOFFER:
319 //reply to dhcp client.
gaurav38351de2016-07-27 04:44:33 +0530320 Ethernet ethernetPacketOffer = processDhcpPacketFrmServer(packet);
321 sendReply(ethernetPacketOffer, dhcpPayload);
gaurav4af95fb2016-07-07 01:48:44 +0530322 break;
323 case DHCPREQUEST:
gaurav38351de2016-07-27 04:44:33 +0530324 //add the gatewayip as virtual interface ip for server to understand the lease to be assigned
325 //and forward the packet to dhcp server.
326 Ethernet ethernetPacketRequest = processDhcpPacketFrmClient(context, packet, clientServerInterfaces);
gaurav4af95fb2016-07-07 01:48:44 +0530327 forwardPacket(ethernetPacketRequest);
328 break;
329 case DHCPACK:
330 //reply to dhcp client.
gaurav38351de2016-07-27 04:44:33 +0530331 Ethernet ethernetPacketAck = processDhcpPacketFrmServer(packet);
332 sendReply(ethernetPacketAck, dhcpPayload);
gauravf0884562016-06-17 02:47:13 +0530333 break;
334 default:
335 break;
336 }
gaurav4af95fb2016-07-07 01:48:44 +0530337 }
338
gaurav38351de2016-07-27 04:44:33 +0530339 //build the DHCP discover/request packet with gatewayip(unicast packet)
340 private Ethernet processDhcpPacketFrmClient(PacketContext context, Ethernet ethernetPacket,
341 Set<Interface> clientInterfaces) {
gaurav4af95fb2016-07-07 01:48:44 +0530342
gaurav38351de2016-07-27 04:44:33 +0530343 //assuming one interface per port for now.
344 relayAgentIP = clientInterfaces.iterator().next().ipAddressesList().get(0).
345 ipAddress().getIp4Address();
346 relayAgentMAC = clientInterfaces.iterator().next().mac();
gaurav4af95fb2016-07-07 01:48:44 +0530347 // get dhcp header.
348 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
gaurav38351de2016-07-27 04:44:33 +0530349 etherReply.setSourceMACAddress(relayAgentMAC);
350 etherReply.setDestinationMACAddress(dhcpServerMac);
gaurav4af95fb2016-07-07 01:48:44 +0530351 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
gaurav38351de2016-07-27 04:44:33 +0530352 ipv4Packet.setSourceAddress(relayAgentIP.toInt());
353 ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530354 UDP udpPacket = (UDP) ipv4Packet.getPayload();
355 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
gaurav38351de2016-07-27 04:44:33 +0530356 dhcpPacket.setGatewayIPAddress(relayAgentIP.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530357 udpPacket.setPayload(dhcpPacket);
358 ipv4Packet.setPayload(udpPacket);
359 etherReply.setPayload(ipv4Packet);
360 return etherReply;
361 }
gauravf0884562016-06-17 02:47:13 +0530362
gaurav38351de2016-07-27 04:44:33 +0530363 //build the DHCP offer/ack with proper client port.
364 private Ethernet processDhcpPacketFrmServer(Ethernet ethernetPacket) {
gaurav4af95fb2016-07-07 01:48:44 +0530365
gaurav38351de2016-07-27 04:44:33 +0530366 // get dhcp header.
367 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
368 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
369 UDP udpPacket = (UDP) ipv4Packet.getPayload();
370 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
371 //set the ethernet frame.
372 etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
373 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
374 udpPacket.setPayload(dhcpPayload);
375 ipv4Packet.setPayload(udpPacket);
376 etherReply.setPayload(ipv4Packet);
377 return etherReply;
378 }
379
380 //send the response to the requestor host.
381 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
382
383 MacAddress descMac = new MacAddress(dhcpPayload.getClientHardwareAddress());
384 Host host = hostService.getHost(HostId.hostId(descMac,
gaurav4af95fb2016-07-07 01:48:44 +0530385 VlanId.vlanId(ethPacket.getVlanID())));
Lei Xu3aeaf722016-08-11 13:51:12 -0700386 //handle the concurrent host offline scenario
387 if (host == null) {
388 return;
389 }
gauravf0884562016-06-17 02:47:13 +0530390 ConnectPoint dhcpRequestor = new ConnectPoint(host.location().elementId(),
391 host.location().port());
392
393 //send Packetout to requestor host.
394 if (dhcpRequestor != null) {
395 TrafficTreatment t = DefaultTrafficTreatment.builder()
396 .setOutput(dhcpRequestor.port()).build();
397 OutboundPacket o = new DefaultOutboundPacket(
gaurav4af95fb2016-07-07 01:48:44 +0530398 dhcpRequestor.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
gauravf0884562016-06-17 02:47:13 +0530399 packetService.emit(o);
400 }
401 }
402 }
403
404 /**
405 * Listener for network config events.
406 */
407 private class InternalConfigListener implements NetworkConfigListener {
408
409 @Override
410 public void event(NetworkConfigEvent event) {
411
412 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
413 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
414 event.configClass().equals(DhcpRelayConfig.class)) {
415 updateConfig();
416 log.info("Reconfigured");
417 }
418 }
419 }
Lei Xu3aeaf722016-08-11 13:51:12 -0700420}