| /* |
| * Copyright 2021-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.onosproject.kubevirtnetworking.impl; |
| |
| import com.google.common.base.Strings; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Lists; |
| import org.onlab.packet.DHCP; |
| import org.onlab.packet.Ethernet; |
| import org.onlab.packet.IPv4; |
| import org.onlab.packet.Ip4Address; |
| import org.onlab.packet.IpAddress; |
| import org.onlab.packet.IpPrefix; |
| import org.onlab.packet.MacAddress; |
| import org.onlab.packet.TpPort; |
| import org.onlab.packet.UDP; |
| import org.onlab.packet.dhcp.DhcpOption; |
| import org.onlab.util.Tools; |
| import org.onosproject.cfg.ComponentConfigService; |
| import org.onosproject.cluster.ClusterService; |
| import org.onosproject.cluster.LeadershipService; |
| import org.onosproject.cluster.NodeId; |
| import org.onosproject.core.ApplicationId; |
| import org.onosproject.core.CoreService; |
| import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService; |
| import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute; |
| import org.onosproject.kubevirtnetworking.api.KubevirtNetwork; |
| import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService; |
| import org.onosproject.kubevirtnetworking.api.KubevirtPort; |
| import org.onosproject.kubevirtnetworking.api.KubevirtPortService; |
| import org.onosproject.kubevirtnode.api.KubevirtNode; |
| import org.onosproject.kubevirtnode.api.KubevirtNodeEvent; |
| import org.onosproject.kubevirtnode.api.KubevirtNodeListener; |
| import org.onosproject.kubevirtnode.api.KubevirtNodeService; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.flow.DefaultTrafficSelector; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.TrafficSelector; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.packet.DefaultOutboundPacket; |
| import org.onosproject.net.packet.PacketContext; |
| import org.onosproject.net.packet.PacketProcessor; |
| import org.onosproject.net.packet.PacketService; |
| import org.osgi.service.component.ComponentContext; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Deactivate; |
| import org.osgi.service.component.annotations.Modified; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ReferenceCardinality; |
| import org.slf4j.Logger; |
| |
| import java.nio.ByteBuffer; |
| import java.util.Dictionary; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.concurrent.ExecutorService; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static java.util.concurrent.Executors.newSingleThreadExecutor; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_BroadcastAddress; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_Classless_Static_Route; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DHCPServerIp; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_DomainServer; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_END; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_LeaseTime; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_RouterAddress; |
| import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_SubnetMask; |
| import static org.onlab.packet.DHCP.MsgType.DHCPACK; |
| import static org.onlab.packet.DHCP.MsgType.DHCPOFFER; |
| import static org.onlab.util.Tools.groupedThreads; |
| import static org.onosproject.kubevirtnetworking.api.Constants.DHCP_TABLE; |
| import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID; |
| import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_DHCP_RULE; |
| import static org.onosproject.kubevirtnetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC; |
| import static org.onosproject.kubevirtnetworking.impl.OsgiPropertyConstants.DHCP_SERVER_MAC_DEFAULT; |
| import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getBroadcastAddr; |
| import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * Handles DHCP requests for the virtual instances. |
| */ |
| @Component( |
| immediate = true, |
| property = { |
| DHCP_SERVER_MAC + "=" + DHCP_SERVER_MAC_DEFAULT |
| } |
| ) |
| public class KubevirtDhcpHandler { |
| protected final Logger log = getLogger(getClass()); |
| |
| private static final Ip4Address DEFAULT_PRIMARY_DNS = Ip4Address.valueOf("8.8.8.8"); |
| private static final Ip4Address DEFAULT_SECONDARY_DNS = Ip4Address.valueOf("8.8.4.4"); |
| private static final byte PACKET_TTL = (byte) 127; |
| |
| // TODO add MTU, static route option codes to ONOS DHCP and remove here |
| private static final byte DHCP_OPTION_MTU = (byte) 26; |
| private static final byte[] DHCP_DATA_LEASE_INFINITE = |
| ByteBuffer.allocate(4).putInt(-1).array(); |
| |
| private static final int OCTET_BIT_LENGTH = 8; |
| private static final int V4_BYTE_SIZE = 4; |
| private static final int V4_CIDR_LOWER_BOUND = -1; |
| private static final int V4_CIDR_UPPER_BOUND = 33; |
| private static final int PADDING_SIZE = 4; |
| |
| private static final byte HARDWARE_ADDR_LENGTH = (byte) 6; |
| private static final byte DHCP_OPTION_DATA_LENGTH = (byte) 4; |
| private static final int DHCP_OPTION_DNS_LENGTH = 8; |
| private static final int DHCP_OPTION_MTU_LENGTH = 2; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected CoreService coreService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected ComponentConfigService configService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected PacketService packetService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected ClusterService clusterService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected LeadershipService leadershipService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected KubevirtNodeService nodeService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected KubevirtPortService portService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected KubevirtNetworkService networkService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY) |
| protected KubevirtFlowRuleService flowService; |
| |
| /** Fake MAC address for virtual network subnet gateway. */ |
| private String dhcpServerMac = DHCP_SERVER_MAC_DEFAULT; |
| |
| private final PacketProcessor packetProcessor = new InternalPacketProcessor(); |
| private final KubevirtNodeListener nodeListener = new InternalNodeEventListener(); |
| |
| private final ExecutorService eventExecutor = newSingleThreadExecutor( |
| groupedThreads(this.getClass().getSimpleName(), "event-handler")); |
| |
| private ApplicationId appId; |
| private NodeId localNodeId; |
| |
| @Activate |
| protected void activate() { |
| appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID); |
| localNodeId = clusterService.getLocalNode().id(); |
| nodeService.addListener(nodeListener); |
| configService.registerProperties(getClass()); |
| packetService.addProcessor(packetProcessor, PacketProcessor.director(0)); |
| leadershipService.runForLeadership(appId.name()); |
| |
| log.info("Started"); |
| } |
| |
| @Deactivate |
| protected void deactivate() { |
| packetService.removeProcessor(packetProcessor); |
| nodeService.removeListener(nodeListener); |
| configService.unregisterProperties(getClass(), false); |
| leadershipService.withdraw(appId.name()); |
| eventExecutor.shutdown(); |
| |
| log.info("Stopped"); |
| } |
| |
| @Modified |
| protected void modified(ComponentContext context) { |
| Dictionary<?, ?> properties = context.getProperties(); |
| String updatedMac; |
| |
| updatedMac = Tools.get(properties, DHCP_SERVER_MAC); |
| |
| if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) { |
| dhcpServerMac = updatedMac; |
| } |
| |
| log.info("Modified"); |
| } |
| |
| private class InternalPacketProcessor implements PacketProcessor { |
| |
| @Override |
| public void process(PacketContext context) { |
| if (context.isHandled()) { |
| return; |
| } |
| |
| Ethernet ethPacket = context.inPacket().parsed(); |
| if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) { |
| return; |
| } |
| IPv4 ipv4Packet = (IPv4) ethPacket.getPayload(); |
| if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_UDP) { |
| return; |
| } |
| UDP udpPacket = (UDP) ipv4Packet.getPayload(); |
| if (udpPacket.getDestinationPort() != UDP.DHCP_SERVER_PORT || |
| udpPacket.getSourcePort() != UDP.DHCP_CLIENT_PORT) { |
| return; |
| } |
| |
| DHCP dhcpPacket = (DHCP) udpPacket.getPayload(); |
| |
| eventExecutor.execute(() -> processDhcp(context, dhcpPacket)); |
| } |
| |
| private void processDhcp(PacketContext context, DHCP dhcpPacket) { |
| if (dhcpPacket == null) { |
| log.trace("DHCP packet without payload received, do nothing"); |
| return; |
| } |
| |
| DHCP.MsgType inPacketType = getPacketType(dhcpPacket); |
| if (inPacketType == null || dhcpPacket.getClientHardwareAddress() == null) { |
| log.trace("Malformed DHCP packet received, ignore it"); |
| return; |
| } |
| |
| MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress()); |
| |
| KubevirtPort port = portService.ports().stream() |
| .filter(p -> p.macAddress().equals(clientMac)) |
| .findAny().orElse(null); |
| |
| if (port == null) { |
| log.debug("Ignore DHCP request, since it comes from unmanaged VM {}", clientMac); |
| return; |
| } |
| |
| IpAddress fixedIp = port.ipAddress(); |
| |
| if (fixedIp == null) { |
| log.warn("There is no IP addresses are assigned with the port {}", port.macAddress()); |
| return; |
| } |
| |
| Ethernet ethPacket = context.inPacket().parsed(); |
| switch (inPacketType) { |
| case DHCPDISCOVER: |
| processDhcpDiscover(context, clientMac, port, ethPacket); |
| break; |
| case DHCPREQUEST: |
| processDhcpRequest(context, clientMac, port, ethPacket); |
| break; |
| case DHCPRELEASE: |
| log.trace("DHCP RELEASE received from {}", clientMac); |
| // do nothing |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private void processDhcpDiscover(PacketContext context, MacAddress clientMac, |
| KubevirtPort port, Ethernet ethPacket) { |
| log.trace("DHCP DISCOVER received from {}", clientMac); |
| Ethernet discoverReply = buildReply(ethPacket, |
| (byte) DHCPOFFER.getValue(), |
| port); |
| sendReply(context, discoverReply); |
| log.trace("DHCP OFFER({}) is sent for {}", port.ipAddress().toString(), clientMac); |
| } |
| |
| private void processDhcpRequest(PacketContext context, MacAddress clientMac, |
| KubevirtPort port, Ethernet ethPacket) { |
| log.trace("DHCP REQUEST received from {}", clientMac); |
| Ethernet requestReply = buildReply(ethPacket, |
| (byte) DHCPACK.getValue(), |
| port); |
| sendReply(context, requestReply); |
| log.trace("DHCP ACK({}) is sent for {}", port.ipAddress(), clientMac); |
| } |
| |
| private DHCP.MsgType getPacketType(DHCP dhcpPacket) { |
| DhcpOption optType = dhcpPacket.getOption(OptionCode_MessageType); |
| if (optType == null) { |
| log.trace("DHCP packet with no message type, ignore it"); |
| return null; |
| } |
| |
| DHCP.MsgType inPacketType = DHCP.MsgType.getType(optType.getData()[0]); |
| if (inPacketType == null) { |
| log.trace("DHCP packet with no packet type, ignore it"); |
| } |
| return inPacketType; |
| } |
| |
| private Ethernet buildReply(Ethernet ethRequest, byte packetType, |
| KubevirtPort port) { |
| log.trace("Build for DHCP reply msg for openstack port {}", port.toString()); |
| |
| // pick one IP address to make a reply |
| // since we check the validity of fixed IP address at parent method, |
| // so no need to double check the fixed IP existence here |
| IpAddress fixedIp = port.ipAddress(); |
| |
| KubevirtNetwork network = networkService.network(port.networkId()); |
| |
| Ethernet ethReply = new Ethernet(); |
| ethReply.setSourceMACAddress(dhcpServerMac); |
| ethReply.setDestinationMACAddress(ethRequest.getSourceMAC()); |
| ethReply.setEtherType(Ethernet.TYPE_IPV4); |
| |
| IPv4 ipv4Request = (IPv4) ethRequest.getPayload(); |
| IPv4 ipv4Reply = new IPv4(); |
| |
| ipv4Reply.setSourceAddress( |
| clusterService.getLocalNode().ip().getIp4Address().toString()); |
| ipv4Reply.setDestinationAddress(fixedIp.toString()); |
| ipv4Reply.setTtl(PACKET_TTL); |
| |
| UDP udpRequest = (UDP) ipv4Request.getPayload(); |
| UDP udpReply = new UDP(); |
| udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT); |
| udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT); |
| |
| DHCP dhcpRequest = (DHCP) udpRequest.getPayload(); |
| DHCP dhcpReply = buildDhcpReply( |
| dhcpRequest, |
| packetType, |
| Ip4Address.valueOf(fixedIp.toString()), port, network); |
| |
| udpReply.setPayload(dhcpReply); |
| ipv4Reply.setPayload(udpReply); |
| ethReply.setPayload(ipv4Reply); |
| |
| return ethReply; |
| } |
| |
| private void sendReply(PacketContext context, Ethernet ethReply) { |
| if (ethReply == null) { |
| return; |
| } |
| ConnectPoint srcPoint = context.inPacket().receivedFrom(); |
| TrafficTreatment treatment = DefaultTrafficTreatment |
| .builder() |
| .setOutput(srcPoint.port()) |
| .build(); |
| |
| packetService.emit(new DefaultOutboundPacket( |
| srcPoint.deviceId(), |
| treatment, |
| ByteBuffer.wrap(ethReply.serialize()))); |
| context.block(); |
| } |
| |
| private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp, |
| KubevirtPort port, KubevirtNetwork network) { |
| Ip4Address gatewayIp = clusterService.getLocalNode().ip().getIp4Address(); |
| int subnetPrefixLen = IpPrefix.valueOf(network.cidr()).prefixLength(); |
| |
| DHCP dhcpReply = new DHCP(); |
| dhcpReply.setOpCode(DHCP.OPCODE_REPLY); |
| dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET); |
| dhcpReply.setHardwareAddressLength(HARDWARE_ADDR_LENGTH); |
| dhcpReply.setTransactionId(request.getTransactionId()); |
| dhcpReply.setFlags(request.getFlags()); |
| dhcpReply.setYourIPAddress(yourIp.toInt()); |
| dhcpReply.setServerIPAddress(gatewayIp.toInt()); |
| dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress()); |
| |
| List<DhcpOption> options = Lists.newArrayList(); |
| |
| // message type |
| options.add(doMsgType(msgType)); |
| |
| // server identifier |
| options.add(doServerId(gatewayIp)); |
| |
| // lease time |
| options.add(doLeaseTime()); |
| |
| // subnet mask |
| options.add(doSubnetMask(subnetPrefixLen)); |
| |
| // broadcast address |
| options.add(doBroadcastAddr(yourIp, subnetPrefixLen)); |
| |
| // domain server |
| options.add(doDomainServer(network)); |
| |
| // mtu |
| options.add(doMtu(network)); |
| |
| // classless static route |
| if (!network.hostRoutes().isEmpty()) { |
| options.add(doClasslessSr(network)); |
| } |
| |
| // Sets the default router address up. |
| // Performs only if the gateway is set in subnet. |
| if (network.gatewayIp() != null) { |
| options.add(doRouterAddr(network)); |
| } |
| |
| // end option |
| options.add(doEnd()); |
| |
| dhcpReply.setOptions(options); |
| return dhcpReply; |
| } |
| |
| |
| private DhcpOption doMsgType(byte msgType) { |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_MessageType.getValue()); |
| option.setLength((byte) 1); |
| byte[] optionData = {msgType}; |
| option.setData(optionData); |
| return option; |
| } |
| |
| private DhcpOption doServerId(IpAddress gatewayIp) { |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_DHCPServerIp.getValue()); |
| option.setLength(DHCP_OPTION_DATA_LENGTH); |
| option.setData(gatewayIp.toOctets()); |
| return option; |
| } |
| |
| private DhcpOption doLeaseTime() { |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_LeaseTime.getValue()); |
| option.setLength(DHCP_OPTION_DATA_LENGTH); |
| option.setData(DHCP_DATA_LEASE_INFINITE); |
| return option; |
| } |
| |
| private DhcpOption doSubnetMask(int subnetPrefixLen) { |
| Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen); |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_SubnetMask.getValue()); |
| option.setLength(DHCP_OPTION_DATA_LENGTH); |
| option.setData(subnetMask.toOctets()); |
| return option; |
| } |
| |
| private DhcpOption doBroadcastAddr(Ip4Address yourIp, int subnetPrefixLen) { |
| String broadcast = getBroadcastAddr(yourIp.toString(), subnetPrefixLen); |
| |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_BroadcastAddress.getValue()); |
| option.setLength(DHCP_OPTION_DATA_LENGTH); |
| option.setData(IpAddress.valueOf(broadcast).toOctets()); |
| |
| return option; |
| } |
| |
| private DhcpOption doDomainServer(KubevirtNetwork network) { |
| DhcpOption option = new DhcpOption(); |
| |
| option.setCode(OptionCode_DomainServer.getValue()); |
| |
| if (network.dnses().isEmpty()) { |
| option.setLength((byte) DHCP_OPTION_DNS_LENGTH); |
| ByteBuffer dnsByteBuf = ByteBuffer.allocate(DHCP_OPTION_DNS_LENGTH); |
| dnsByteBuf.put(DEFAULT_PRIMARY_DNS.toOctets()); |
| dnsByteBuf.put(DEFAULT_SECONDARY_DNS.toOctets()); |
| |
| option.setData(dnsByteBuf.array()); |
| } else { |
| int dnsLength = 4 * network.dnses().size(); |
| |
| option.setLength((byte) dnsLength); |
| |
| ByteBuffer dnsByteBuf = ByteBuffer.allocate(DHCP_OPTION_DNS_LENGTH); |
| |
| for (IpAddress dnsServer : network.dnses()) { |
| dnsByteBuf.put(dnsServer.toOctets()); |
| } |
| option.setData(dnsByteBuf.array()); |
| } |
| |
| return option; |
| } |
| |
| private DhcpOption doMtu(KubevirtNetwork network) { |
| DhcpOption option = new DhcpOption(); |
| option.setCode(DHCP_OPTION_MTU); |
| option.setLength((byte) DHCP_OPTION_MTU_LENGTH); |
| checkNotNull(network); |
| checkNotNull(network.mtu()); |
| |
| option.setData(ByteBuffer.allocate(DHCP_OPTION_MTU_LENGTH) |
| .putShort(network.mtu().shortValue()).array()); |
| |
| return option; |
| } |
| |
| private DhcpOption doClasslessSr(KubevirtNetwork network) { |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_Classless_Static_Route.getValue()); |
| |
| int hostRoutesSize = hostRoutesSize(ImmutableList.copyOf(network.hostRoutes())); |
| if (hostRoutesSize == 0) { |
| throw new IllegalArgumentException("Illegal CIDR hostRoutesSize value!"); |
| } |
| |
| log.trace("hostRouteSize: {}", hostRoutesSize); |
| |
| option.setLength((byte) hostRoutesSize); |
| ByteBuffer hostRouteByteBuf = ByteBuffer.allocate(hostRoutesSize); |
| |
| network.hostRoutes().forEach(h -> { |
| log.debug("processing host route information: {}", h.toString()); |
| |
| IpPrefix ipPrefix = h.destination(); |
| |
| hostRouteByteBuf.put(Objects.requireNonNull(bytesDestinationDescriptor(ipPrefix))); |
| hostRouteByteBuf.put(h.nexthop().toOctets()); |
| }); |
| |
| option.setData(hostRouteByteBuf.array()); |
| return option; |
| } |
| |
| private DhcpOption doRouterAddr(KubevirtNetwork network) { |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_RouterAddress.getValue()); |
| option.setLength(DHCP_OPTION_DATA_LENGTH); |
| option.setData(Ip4Address.valueOf(network.gatewayIp().toString()).toOctets()); |
| return option; |
| } |
| |
| private DhcpOption doEnd() { |
| DhcpOption option = new DhcpOption(); |
| option.setCode(OptionCode_END.getValue()); |
| option.setLength((byte) 1); |
| return option; |
| } |
| |
| private int hostRoutesSize(List<KubevirtHostRoute> hostRoutes) { |
| int size = 0; |
| int preFixLen; |
| |
| for (KubevirtHostRoute h : hostRoutes) { |
| preFixLen = h.destination().prefixLength(); |
| if (Math.max(V4_CIDR_LOWER_BOUND, preFixLen) == V4_CIDR_LOWER_BOUND || |
| Math.min(preFixLen, V4_CIDR_UPPER_BOUND) == V4_CIDR_UPPER_BOUND) { |
| throw new IllegalArgumentException("Illegal CIDR length value!"); |
| } |
| |
| for (int i = 0; i <= V4_BYTE_SIZE; i++) { |
| if (preFixLen == Math.min(preFixLen, i * OCTET_BIT_LENGTH)) { |
| size = size + i + 1 + PADDING_SIZE; |
| break; |
| } |
| } |
| } |
| return size; |
| } |
| |
| private byte[] bytesDestinationDescriptor(IpPrefix ipPrefix) { |
| ByteBuffer byteBuffer; |
| int prefixLen = ipPrefix.prefixLength(); |
| |
| // retrieve ipPrefix to the destination descriptor format |
| // ex) 10.1.1.0/24 -> [10,1,1,0] |
| String[] ipPrefixString = ipPrefix.getIp4Prefix().toString() |
| .split("/")[0] |
| .split("\\."); |
| |
| // retrieve destination descriptor and put this to byte buffer |
| // according to RFC 3442 |
| // ex) 0.0.0.0/0 -> 0 |
| // ex) 10.0.0.0/8 -> 8.10 |
| // ex) 10.17.0.0/16 -> 16.10.17 |
| // ex) 10.27.129.0/24 -> 24.10.27.129 |
| // ex) 10.229.0.128/25 -> 25.10.229.0.128 |
| for (int i = 0; i <= V4_BYTE_SIZE; i++) { |
| if (prefixLen == Math.min(prefixLen, i * OCTET_BIT_LENGTH)) { |
| byteBuffer = ByteBuffer.allocate(i + 1); |
| byteBuffer.put((byte) prefixLen); |
| |
| for (int j = 0; j < i; j++) { |
| byteBuffer.put((byte) Integer.parseInt(ipPrefixString[j])); |
| } |
| return byteBuffer.array(); |
| } |
| } |
| |
| return null; |
| } |
| } |
| |
| private class InternalNodeEventListener implements KubevirtNodeListener { |
| |
| @Override |
| public boolean isRelevant(KubevirtNodeEvent event) { |
| return event.subject().type() == WORKER; |
| } |
| |
| private boolean isRelevantHelper() { |
| return Objects.equals(localNodeId, leadershipService.getLeader(appId.name())); |
| } |
| |
| @Override |
| public void event(KubevirtNodeEvent event) { |
| KubevirtNode node = event.subject(); |
| switch (event.type()) { |
| case KUBEVIRT_NODE_COMPLETE: |
| eventExecutor.execute(() -> processNodeCompletion(node)); |
| break; |
| case KUBEVIRT_NODE_CREATED: |
| case KUBEVIRT_NODE_INCOMPLETE: |
| case KUBEVIRT_NODE_REMOVED: |
| case KUBEVIRT_NODE_UPDATED: |
| default: |
| break; |
| } |
| } |
| |
| private void processNodeCompletion(KubevirtNode node) { |
| if (!isRelevantHelper()) { |
| return; |
| } |
| setDhcpRule(node, true); |
| } |
| |
| private void setDhcpRule(KubevirtNode node, boolean install) { |
| TrafficSelector selector = DefaultTrafficSelector.builder() |
| .matchEthType(Ethernet.TYPE_IPV4) |
| .matchIPProtocol(IPv4.PROTOCOL_UDP) |
| .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT)) |
| .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT)) |
| .build(); |
| |
| TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| .punt() |
| .build(); |
| |
| flowService.setRule( |
| appId, |
| node.intgBridge(), |
| selector, |
| treatment, |
| PRIORITY_DHCP_RULE, |
| DHCP_TABLE, |
| install); |
| } |
| } |
| } |