Kalhee Kim | 495c9b2 | 2017-11-07 16:32:09 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2017-present Open Networking Foundation |
| 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 | * |
| 16 | */ |
| 17 | package org.onosproject.dhcprelay; |
| 18 | |
| 19 | import org.onlab.packet.BasePacket; |
| 20 | import org.onlab.packet.DHCP6; |
| 21 | import org.onlab.packet.DHCP6.MsgType; |
| 22 | import org.onlab.packet.Ip6Address; |
| 23 | import org.onlab.packet.IpAddress; |
| 24 | import org.onlab.packet.VlanId; |
| 25 | import org.onlab.packet.dhcp.Dhcp6RelayOption; |
| 26 | import org.onlab.packet.dhcp.Dhcp6Option; |
| 27 | |
| 28 | import org.onlab.packet.Ethernet; |
| 29 | import org.onlab.packet.IPv6; |
| 30 | import org.onlab.packet.MacAddress; |
| 31 | import org.onlab.packet.UDP; |
| 32 | |
| 33 | import org.onlab.util.HexString; |
| 34 | import org.onosproject.dhcprelay.api.DhcpServerInfo; |
| 35 | import org.onosproject.net.ConnectPoint; |
| 36 | import org.onosproject.net.host.InterfaceIpAddress; |
| 37 | import org.onosproject.net.intf.Interface; |
| 38 | import org.onosproject.net.packet.PacketContext; |
| 39 | import org.onosproject.net.DeviceId; |
| 40 | |
| 41 | import org.slf4j.Logger; |
| 42 | import org.slf4j.LoggerFactory; |
| 43 | import java.util.Set; |
| 44 | import java.util.List; |
| 45 | import java.util.ArrayList; |
| 46 | |
| 47 | |
| 48 | |
| 49 | import static com.google.common.base.Preconditions.checkNotNull; |
| 50 | |
| 51 | |
| 52 | |
| 53 | public class Dhcp6HandlerUtil { |
| 54 | |
| 55 | private final Logger log = LoggerFactory.getLogger(getClass()); |
| 56 | // Returns the first v6 interface ip out of a set of interfaces or null. |
| 57 | // Checks all interfaces, and ignores v6 interface ips |
| 58 | public Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) { |
| 59 | for (Interface intf : intfs) { |
| 60 | for (InterfaceIpAddress ip : intf.ipAddressesList()) { |
| 61 | Ip6Address relayAgentIp = ip.ipAddress().getIp6Address(); |
| 62 | if (relayAgentIp != null) { |
| 63 | return relayAgentIp; |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | return null; |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Returns the first interface ip from interface. |
| 72 | * |
| 73 | * @param iface interface of one connect point |
| 74 | * @return the first interface IP; null if not exists an IP address in |
| 75 | * these interfaces |
| 76 | */ |
| 77 | public Ip6Address getFirstIpFromInterface(Interface iface) { |
| 78 | checkNotNull(iface, "Interface can't be null"); |
| 79 | return iface.ipAddressesList().stream() |
| 80 | .map(InterfaceIpAddress::ipAddress) |
| 81 | .filter(IpAddress::isIp6) |
| 82 | .map(IpAddress::getIp6Address) |
| 83 | .findFirst() |
| 84 | .orElse(null); |
| 85 | } |
| 86 | |
| 87 | /** |
| 88 | * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply. |
| 89 | * |
| 90 | * @param dhcp6 dhcp6 relay-reply or relay-foward |
| 91 | * @return dhcp6Packet dhcp6 packet extracted from relay-message |
| 92 | */ |
| 93 | public DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) { |
| 94 | |
| 95 | // extract the relay message if exist |
| 96 | DHCP6 dhcp6Payload = dhcp6.getOptions().stream() |
| 97 | .filter(opt -> opt instanceof Dhcp6RelayOption) |
| 98 | .map(BasePacket::getPayload) |
| 99 | .map(pld -> (DHCP6) pld) |
| 100 | .findFirst() |
| 101 | .orElse(null); |
| 102 | if (dhcp6Payload == null) { |
| 103 | // Can't find dhcp payload |
| 104 | log.debug("Can't find dhcp6 payload from relay message"); |
| 105 | } else { |
| 106 | log.debug("dhcp6 payload found from relay message {}", dhcp6Payload); |
| 107 | } |
| 108 | return dhcp6Payload; |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * find the leaf DHCP6 packet from multi-level relay packet. |
| 113 | * |
| 114 | * @param relayPacket dhcp6 relay packet |
| 115 | * @return leafPacket non-relay dhcp6 packet |
| 116 | */ |
| 117 | public DHCP6 getDhcp6Leaf(DHCP6 relayPacket) { |
| 118 | DHCP6 dhcp6Parent = relayPacket; |
| 119 | DHCP6 dhcp6Child = null; |
| 120 | |
| 121 | log.debug("getDhcp6Leaf entered."); |
| 122 | while (dhcp6Parent != null) { |
| 123 | dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent); |
| 124 | if (dhcp6Child != null) { |
| 125 | if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() && |
| 126 | dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) { |
| 127 | log.debug("leaf dhcp6 packet found."); |
| 128 | break; |
| 129 | } else { |
| 130 | // found another relay, go for another loop |
| 131 | dhcp6Parent = dhcp6Child; |
| 132 | } |
| 133 | } else { |
| 134 | log.debug("Expected dhcp6 within relay pkt, but no dhcp6 leaf found."); |
| 135 | break; |
| 136 | } |
| 137 | } |
| 138 | return dhcp6Child; |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * check if DHCP6 relay-reply is reply. |
| 143 | * |
| 144 | * @param relayPacket dhcp6 relay-reply |
| 145 | * @return boolean relay-reply contains ack |
| 146 | */ |
| 147 | public boolean isDhcp6Reply(DHCP6 relayPacket) { |
| 148 | DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket); |
| 149 | if (leafDhcp6 != null) { |
| 150 | if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) { |
| 151 | log.debug("isDhcp6Reply true."); |
| 152 | return true; // must be directly connected |
| 153 | } else { |
| 154 | log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType()); |
| 155 | } |
| 156 | } else { |
| 157 | log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found."); |
| 158 | } |
| 159 | log.debug("isDhcp6Reply false."); |
| 160 | return false; |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * check if DHCP6 is release or relay-forward contains release. |
| 165 | * |
| 166 | * @param dhcp6Payload dhcp6 packet |
| 167 | * @return boolean dhcp6 contains release |
| 168 | */ |
| 169 | public boolean isDhcp6Release(DHCP6 dhcp6Payload) { |
| 170 | if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) { |
| 171 | log.debug("isDhcp6Release true."); |
| 172 | return true; // must be directly connected |
| 173 | } else { |
| 174 | DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload); |
| 175 | if (dhcp6Leaf != null) { |
| 176 | if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) { |
| 177 | log.debug("isDhcp6Release true. indirectlry connected"); |
| 178 | return true; |
| 179 | } else { |
| 180 | log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType()); |
| 181 | return false; |
| 182 | } |
| 183 | } else { |
| 184 | log.debug("isDhcp6Release false. dhcp6 is niether relay nor release."); |
| 185 | return false; |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | |
| 191 | /** |
| 192 | * convert dhcp6 msgType to String. |
| 193 | * |
| 194 | * @param msgTypeVal msgType byte of dhcp6 packet |
| 195 | * @return String string value of dhcp6 msg type |
| 196 | */ |
| 197 | public String getMsgTypeStr(byte msgTypeVal) { |
| 198 | MsgType msgType = DHCP6.MsgType.getType(msgTypeVal); |
| 199 | return DHCP6.MsgType.getMsgTypeStr(msgType); |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * find the string of dhcp6 leaf packets's msg type. |
| 204 | * |
| 205 | * @param directConnFlag boolean value indicating direct/indirect connection |
| 206 | * @param dhcp6Packet dhcp6 packet |
| 207 | * @return String string value of dhcp6 leaf packet msg type |
| 208 | */ |
| 209 | public String findLeafMsgType(boolean directConnFlag, DHCP6 dhcp6Packet) { |
| 210 | if (directConnFlag) { |
| 211 | return getMsgTypeStr(dhcp6Packet.getMsgType()); |
| 212 | } else { |
| 213 | DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet); |
| 214 | if (leafDhcp != null) { |
| 215 | return getMsgTypeStr(leafDhcp.getMsgType()); |
| 216 | } else { |
| 217 | return "INVALID"; //DhcpRelayCounters.INVALID_PACKET; |
| 218 | } |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | /** |
| 223 | * find the string of dhcp6 leaf packets's msg type. |
| 224 | * |
| 225 | * @param fromClient indicate from what side a packet is received |
| 226 | * @param directConnFlag boolean value indicating direct/indirect connection |
| 227 | * @param dhcp6Packet dhcp6 packet |
| 228 | * @return String string value of dhcp6 leaf packet msg type |
| 229 | */ |
| 230 | public String findMsgType(boolean fromClient, boolean directConnFlag, DHCP6 dhcp6Packet) { |
| 231 | if (fromClient) { |
| 232 | return findLeafMsgType(directConnFlag, dhcp6Packet); |
| 233 | } else { |
| 234 | DHCP6 embeddedDhcp6 = dhcp6Packet.getOptions().stream() |
| 235 | .filter(opt -> opt instanceof Dhcp6RelayOption) |
| 236 | .map(BasePacket::getPayload) |
| 237 | .map(pld -> (DHCP6) pld) |
| 238 | .findFirst() |
| 239 | .orElse(null); |
| 240 | if (embeddedDhcp6 != null) { |
| 241 | return findLeafMsgType(directConnFlag, embeddedDhcp6); |
| 242 | } else { |
| 243 | return "INVALID"; //DhcpRelayCounters.INVALID_PACKET; |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | /** |
| 249 | * Determind if an Interface contains a vlan id. |
| 250 | * |
| 251 | * @param iface the Interface |
| 252 | * @param vlanId the vlan id |
| 253 | * @return true if the Interface contains the vlan id |
| 254 | */ |
| 255 | public boolean interfaceContainsVlan(Interface iface, VlanId vlanId) { |
| 256 | if (vlanId.equals(VlanId.NONE)) { |
| 257 | // untagged packet, check if vlan untagged or vlan native is not NONE |
| 258 | return !iface.vlanUntagged().equals(VlanId.NONE) || |
| 259 | !iface.vlanNative().equals(VlanId.NONE); |
| 260 | } |
| 261 | // tagged packet, check if the interface contains the vlan |
| 262 | return iface.vlanTagged().contains(vlanId); |
| 263 | } |
| 264 | |
| 265 | /** |
| 266 | * the new class the contains Ethernet packet and destination port. |
| 267 | */ |
| 268 | public class InternalPacket { |
| 269 | Ethernet packet; |
| 270 | ConnectPoint destLocation; |
| 271 | public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) { |
| 272 | packet = newPacket; |
| 273 | destLocation = newLocation; |
| 274 | } |
| 275 | void setLocation(ConnectPoint newLocation) { |
| 276 | destLocation = newLocation; |
| 277 | } |
| 278 | } |
| 279 | /** |
| 280 | * Check if the host is directly connected to the network or not. |
| 281 | * |
| 282 | * @param dhcp6Payload the dhcp6 payload |
| 283 | * @return true if the host is directly connected to the network; false otherwise |
| 284 | */ |
| 285 | public boolean directlyConnected(DHCP6 dhcp6Payload) { |
| 286 | log.debug("directlyConnected enters"); |
| 287 | |
| 288 | if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() && |
| 289 | dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) { |
| 290 | log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType()); |
| 291 | |
| 292 | return true; |
| 293 | } |
| 294 | // Regardless of relay-forward or relay-replay, check if we see another relay message |
| 295 | DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload); |
| 296 | if (dhcp6Payload2 != null) { |
| 297 | if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) { |
| 298 | log.debug("directlyConnected false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType()); |
| 299 | return false; |
| 300 | } else { |
| 301 | // relay-reply |
| 302 | if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) { |
| 303 | log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType()); |
| 304 | return true; // must be directly connected |
| 305 | } else { |
| 306 | log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}", |
| 307 | dhcp6Payload2.getMsgType()); |
| 308 | return false; // must be indirectly connected |
| 309 | } |
| 310 | } |
| 311 | } else { |
| 312 | log.debug("directlyConnected true."); |
| 313 | return true; |
| 314 | } |
| 315 | } |
| 316 | /** |
| 317 | * Check if a given server info has v6 ipaddress. |
| 318 | * |
| 319 | * @param serverInfo server info to check |
| 320 | * @return true if server info has v6 ip address; false otherwise |
| 321 | */ |
| 322 | public boolean isServerIpEmpty(DhcpServerInfo serverInfo) { |
| 323 | if (!serverInfo.getDhcpServerIp6().isPresent()) { |
| 324 | log.warn("DhcpServerIp not available, use default DhcpServerIp {}", |
| 325 | HexString.toHexString(serverInfo.getDhcpServerIp6().get().toOctets())); |
| 326 | return true; |
| 327 | } |
| 328 | return false; |
| 329 | } |
| 330 | |
| 331 | private boolean isConnectMacEmpty(DhcpServerInfo serverInfo, Set<Interface> clientInterfaces) { |
| 332 | if (!serverInfo.getDhcpConnectMac().isPresent()) { |
| 333 | log.warn("DHCP6 {} not yet resolved .. Aborting DHCP " |
| 334 | + "packet processing from client on port: {}", |
| 335 | !serverInfo.getDhcpGatewayIp6().isPresent() ? "server IP " + serverInfo.getDhcpServerIp6() |
| 336 | : "gateway IP " + serverInfo.getDhcpGatewayIp6(), |
| 337 | clientInterfaces.iterator().next().connectPoint()); |
| 338 | return true; |
| 339 | } |
| 340 | return false; |
| 341 | } |
| 342 | |
| 343 | private boolean isRelayAgentIpFromCfgEmpty(DhcpServerInfo serverInfo, DeviceId receivedFromDevice) { |
| 344 | if (!serverInfo.getRelayAgentIp6(receivedFromDevice).isPresent()) { |
| 345 | log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic."); |
| 346 | return true; |
| 347 | } |
| 348 | return false; |
| 349 | } |
| 350 | |
| 351 | private Dhcp6Option getInterfaceIdIdOption(PacketContext context, Ethernet clientPacket) { |
| 352 | String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":"; |
| 353 | Dhcp6Option interfaceId = new Dhcp6Option(); |
| 354 | interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value()); |
| 355 | byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress(); |
| 356 | byte[] inPortStringBytes = inPortString.getBytes(); |
| 357 | byte[] vlanIdBytes = new byte[2]; |
| 358 | vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff); |
| 359 | vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff); |
| 360 | byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length + |
| 361 | inPortStringBytes.length + vlanIdBytes.length]; |
| 362 | log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}", |
| 363 | interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length, |
| 364 | vlanIdBytes.length); |
| 365 | |
| 366 | System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length); |
| 367 | System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, |
| 368 | inPortStringBytes.length); |
| 369 | System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, |
| 370 | clientSoureMacBytes.length + inPortStringBytes.length, |
| 371 | vlanIdBytes.length); |
| 372 | interfaceId.setData(interfaceIdBytes); |
| 373 | interfaceId.setLength((short) interfaceIdBytes.length); |
| 374 | log.debug("interfaceId write srcMac {} portString {}", |
| 375 | HexString.toHexString(clientSoureMacBytes, ":"), inPortString); |
| 376 | return interfaceId; |
| 377 | } |
| 378 | |
| 379 | private void addDhcp6OptionsFromClient(List<Dhcp6Option> options, byte[] dhcp6PacketByte, |
| 380 | PacketContext context, Ethernet clientPacket) { |
| 381 | Dhcp6Option relayMessage = new Dhcp6Option(); |
| 382 | relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value()); |
| 383 | relayMessage.setLength((short) dhcp6PacketByte.length); |
| 384 | relayMessage.setData(dhcp6PacketByte); |
| 385 | options.add(relayMessage); |
| 386 | // create interfaceId option |
| 387 | Dhcp6Option interfaceId = getInterfaceIdIdOption(context, clientPacket); |
| 388 | options.add(interfaceId); |
| 389 | } |
| 390 | |
| 391 | /** |
| 392 | * build the DHCP6 solicit/request packet with gatewayip. |
| 393 | * |
| 394 | * @param context packet context |
| 395 | * @param clientPacket client ethernet packet |
| 396 | * @param clientInterfaces set of client side interfaces |
| 397 | * @param serverInfo target server which a packet is generated for |
| 398 | * @param serverInterface target server interface |
| 399 | * @return ethernet packet with dhcp6 packet info |
| 400 | */ |
| 401 | public Ethernet buildDhcp6PacketFromClient(PacketContext context, Ethernet clientPacket, |
| 402 | Set<Interface> clientInterfaces, DhcpServerInfo serverInfo, |
| 403 | Interface serverInterface) { |
| 404 | ConnectPoint receivedFrom = context.inPacket().receivedFrom(); |
| 405 | DeviceId receivedFromDevice = receivedFrom.deviceId(); |
| 406 | |
| 407 | Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces); |
| 408 | MacAddress relayAgentMac = clientInterfaces.iterator().next().mac(); |
| 409 | if (relayAgentIp == null || relayAgentMac == null) { |
| 410 | log.warn("Missing DHCP relay agent interface Ipv6 addr config for " |
| 411 | + "packet from client on port: {}. Aborting packet processing", |
| 412 | clientInterfaces.iterator().next().connectPoint()); |
| 413 | return null; |
| 414 | } |
| 415 | IPv6 clientIpv6 = (IPv6) clientPacket.getPayload(); |
| 416 | UDP clientUdp = (UDP) clientIpv6.getPayload(); |
| 417 | DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload(); |
| 418 | boolean directConnFlag = directlyConnected(clientDhcp6); |
| 419 | |
| 420 | Ip6Address serverIpFacing = getFirstIpFromInterface(serverInterface); |
| 421 | if (serverIpFacing == null || serverInterface.mac() == null) { |
| 422 | log.warn("No IP v6 address for server Interface {}", serverInterface); |
| 423 | return null; |
| 424 | } |
| 425 | |
| 426 | Ethernet etherReply = clientPacket.duplicate(); |
| 427 | etherReply.setSourceMACAddress(serverInterface.mac()); |
| 428 | |
| 429 | // set default info and replace with indirect if available later on. |
| 430 | if (serverInfo.getDhcpConnectMac().isPresent()) { |
| 431 | etherReply.setDestinationMACAddress(serverInfo.getDhcpConnectMac().get()); |
| 432 | } |
| 433 | if (serverInfo.getDhcpConnectVlan().isPresent()) { |
| 434 | etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort()); |
| 435 | } |
| 436 | IPv6 ipv6Packet = (IPv6) etherReply.getPayload(); |
| 437 | byte[] peerAddress = clientIpv6.getSourceAddress(); |
| 438 | ipv6Packet.setSourceAddress(serverIpFacing.toOctets()); |
| 439 | ipv6Packet.setDestinationAddress(serverInfo.getDhcpServerIp6().get().toOctets()); |
| 440 | UDP udpPacket = (UDP) ipv6Packet.getPayload(); |
| 441 | udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT); |
| 442 | DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload(); |
| 443 | byte[] dhcp6PacketByte = dhcp6Packet.serialize(); |
| 444 | |
| 445 | DHCP6 dhcp6Relay = new DHCP6(); |
| 446 | |
| 447 | dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value()); |
| 448 | |
| 449 | if (directConnFlag) { |
| 450 | dhcp6Relay.setLinkAddress(relayAgentIp.toOctets()); |
| 451 | } else { |
| 452 | if (isServerIpEmpty(serverInfo)) { |
| 453 | log.warn("indirect DhcpServerIp empty... use default server "); |
| 454 | } else { |
| 455 | // Indirect case, replace destination to indirect dhcp server if exist |
| 456 | // Check if mac is obtained for valid server ip |
| 457 | if (isConnectMacEmpty(serverInfo, clientInterfaces)) { |
| 458 | log.warn("indirect Dhcp ConnectMac empty ..."); |
| 459 | return null; |
| 460 | } |
| 461 | etherReply.setDestinationMACAddress(serverInfo.getDhcpConnectMac().get()); |
| 462 | etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort()); |
| 463 | ipv6Packet.setDestinationAddress(serverInfo.getDhcpServerIp6().get().toOctets()); |
| 464 | } |
| 465 | if (isRelayAgentIpFromCfgEmpty(serverInfo, receivedFromDevice)) { |
| 466 | dhcp6Relay.setLinkAddress(relayAgentIp.toOctets()); |
| 467 | log.debug("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}", |
| 468 | HexString.toHexString(relayAgentIp.toOctets(), ":")); |
| 469 | } else { |
| 470 | dhcp6Relay.setLinkAddress(serverInfo.getRelayAgentIp6(receivedFromDevice).get().toOctets()); |
| 471 | } |
| 472 | } |
| 473 | // peer address: address of the client or relay agent from which the message to be relayed was received. |
| 474 | dhcp6Relay.setPeerAddress(peerAddress); |
| 475 | // directly connected case, hop count is zero; otherwise, hop count + 1 |
| 476 | if (directConnFlag) { |
| 477 | dhcp6Relay.setHopCount((byte) 0); |
| 478 | } else { |
| 479 | dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1)); |
| 480 | } |
| 481 | |
| 482 | List<Dhcp6Option> options = new ArrayList<>(); |
| 483 | addDhcp6OptionsFromClient(options, dhcp6PacketByte, context, clientPacket); |
| 484 | dhcp6Relay.setOptions(options); |
| 485 | udpPacket.setPayload(dhcp6Relay); |
| 486 | udpPacket.resetChecksum(); |
| 487 | ipv6Packet.setPayload(udpPacket); |
| 488 | ipv6Packet.setHopLimit((byte) 64); |
| 489 | etherReply.setPayload(ipv6Packet); |
| 490 | |
| 491 | return etherReply; |
| 492 | } |
| 493 | |
| 494 | /** |
| 495 | * build the DHCP6 solicit/request packet with gatewayip. |
| 496 | * |
| 497 | * @param directConnFlag flag indicating if packet is from direct client or not |
| 498 | * @param serverInfo server to check its connect point |
| 499 | * @return boolean true if serverInfo is found; false otherwise |
| 500 | */ |
| 501 | public boolean checkDhcpServerConnPt(boolean directConnFlag, |
| 502 | DhcpServerInfo serverInfo) { |
| 503 | if (serverInfo.getDhcpServerConnectPoint() == null) { |
| 504 | log.warn("DHCP6 server connect point for {} connPt {}", |
| 505 | directConnFlag ? "direct" : "indirect", serverInfo.getDhcpServerConnectPoint()); |
| 506 | return false; |
| 507 | } |
| 508 | return true; |
| 509 | } |
| 510 | } |