Настройка разбора опции DHCP Relay Option 82

Извлечение значений, идентифицирующих абонента из DHCP-пакета

Для корректной работы нужно правильно извлекать значения agentRemoteId, circuitId (port/VLAN) из DHCP-пакета. А в случае использования IPoE c Cisco ISG или SmartEdge еще и из RADIUS-пакетов (в этом случае субопции опции 82 находятся внутри пакетов).

# Нужно указать код субопции опции 82 для извлечения значений
# agentRemoteId обычно находится в субопции 2
dhcp.option82.agentRemoteId.code=2
# interfaceId обычно находится в субопции 1 (circuitId)
dhcp.option82.interfaceId.code=1
# vlanId обычно находится в субопции 1 (circuitId)
dhcp.option82.vlanId.code=1
# Если в субопции отсутствует заголовок с длиной субопции, то укажите 0. Иначе укажите длину заголовка.
# Данный параметр используется в том числе, для того, чтобы извлеченные значения circuitId из DHCP-пакета и из RADIUS-пакета были идентичны.
# Соответственно, значение position нужно указывать относительно removeHeader.
dhcp.option82.removeHeader=0
# Нужно указать параметры извлечения agentRemoteId из субопции (dhcp.option82.agentRemoteId.code)
# позиция значения внутри субопции
dhcp.option82.agentRemoteId.position=2
# если длина значения может изменятся и нужно брать значение до конца субопции, то укажите -1
dhcp.option82.agentRemoteId.length=6
# 0, если remoteId в бинарном виде, например, MAC-адрес; 1, если там закодирована строка
dhcp.option82.agentRemoteId.type=0
# Нужно указать параметры извлечения interfaceId из субопции (dhcp.option82.interfaceId.code)
dhcp.option82.interfaceId.position=5
dhcp.option82.interfaceId.length=1
# Нужно указать параметры извлечения interfaceId из субопции (dhcp.option82.vlanId.code)
dhcp.option82.vlanId.position=2
dhcp.option82.vlanId.length=2

Разные типы устройств

Если используются разные типы устройств, у которых разные форматы circuitId, тип поиска DHCP-устройства должен быть 0 (в этом режиме сначала находится устройство по giaddr, у него вызывается preprocess, затем находится агентское ус-во, у него тоже вызывается preprocess) или 1 (в этом режиме сначала находится устройство по giaddr, затем находится агентское ус-во, у него тоже вызывается preprocess).

dhcp.deviceSearchMode=0

Конфигурация парсинга agentRemoteId должна быть указана в устройстве, с которого приходит запрос на InetAccess (т.е. чей giaddr указан в пакете). А конфигурация извлечения порта/VLAN из curcuitId должна быть указана в агентских типах устройствах. Таким образом InetAccess найдет relay agent по giaddr, по его конфигурации извлечет agentRemoteId, по agentRemoteId найдет дочернее агентское устройство и уже по его конфигурации извлечет значения порта/VLAN.

Разные типы устройств c разным форматом agentRemoteId

В этом случае нельзя однозначно указать в конфигурации как извлечь agentRemoteId, в отличие от варианта выше. Поэтому нужно воспользоваться предобработкой пакетов. Укажите тип поиска DHCP-устройства = 0 (в этом режиме сначала находится устройство по giaddr, у него вызывается preprocess, затем находится агентское ус-во, у него тоже вызывается preprocess).

dhcp.deviceSearchMode=0

Расширьте обработчик процессора протокола типа устройства, с с которого приходит запрос на InetAccess (т.е. чей giaddr указан в пакете). По giaddr InetAccess однозначно найдет устройство. Затем вызовет у него предобработку, в которой нужно определить как распарсить и в ручную проставить agentRemoteId.

import java.util.Arrays;

import ru.avantis.abilling.kernel.network.dhcp.DhcpPacket;
import ru.avantis.abilling.kernel.network.dhcp.DhcpProtocolHandler;
import ru.avantis.abilling.modules.inet.access.sa.ProtocolHandlerAdapter;
import ru.avantis.abilling.modules.inet.dhcp.InetDhcpProcessor;

public class Dhcp82ProtocolHandler
extends ProtocolHandlerAdapter
implements DhcpProtocolHandler
{
@Override
public void preprocessDhcpRequest( DhcpPacket request, DhcpPacket response )
throws Exception
{
byte[] agentRemoteId = request.getSubOption( (byte)2 ).value;
// DLink
if( agentRemoteId.length == 8 )
{
request.setOption( InetDhcpProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 2, 6 ) );
}
else
{
request.setOption( InetDhcpProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 5, 6 ) );
}
}
}

Конфигурация извлечения порта/VLAN из curcuitId должна быть указана в агентских типах устройствах. Таким образом InetAccess найдет relay agent по giaddr, предобработка извлечет и проставит agentRemoteId, по agentRemoteId InetAccess найдет дочернее агентское устройство и уже по его конфигурации извлечет значения порта/VLAN.

Cisco ISG и SmartEdge

В отличие от схемы DHCP82 без Cisco ISG/SmartEdge, здесь еще нужно извлечь remoteId и circuitId из RADIUS-пакета.

# Если в значении атрибута отсутствует заголовок с длиной субопции, то укажите 0. Иначе укажите длину заголовка.
# Данный параметр используется в том числе, для того, чтобы извлеченные значения circuitId из DHCP-пакета и из RADIUS-пакета были идентичны.
# Соответственно, значение position нужно указывать относительно removeHeader.
radius.agent.option.removeHeader=2
# SmartEdge
# код атрибута
radius.agent.option.remoteId.type=96
# позиция в значении атрибута
radius.agent.option.remoteId.position=0
# длина
radius.agent.option.remoteId.length=-1
radius.agent.option.circuitId.type=97

# или
radius.agent.option.remoteId.type=202
radius.agent.option.remoteId.position=0
radius.agent.option.remoteId.length=-1
radius.agent.option.circuitId.type=202
# Cisco ISG
radius.agent.option.remoteId.type=1
radius.agent.option.remoteId.prefix=remote-id-tag=
radius.agent.option.circuitId.type=1
radius.agent.option.circuitId.prefix=circuit-id-tag=

Cisco ISG и SmartEdge и разные типы устройств c разным форматом agentRemoteId

В этом случае нельзя однозначно указать в конфигурации как извлечь agentRemoteId и curcuitId из RADIUS-пакета. Поэтому это нужно сделать в предобработке (извлечение из RADIUS-пакета и сейчас происходит в предобработке, но согласно конфигурации - это можно увидеть в динамических классах ISGProtocolHandler и SmartEdgeClipsProtocolHandler). Расширьте класс предобработки Cisco ISG/SmartEdge:

import java.util.Arrays;

import ru.avantis.abilling.kernel.network.radius.RadiusAttribute;
import ru.avantis.abilling.kernel.network.radius.RadiusPacket;
import ru.avantis.abilling.modules.inet.radius.InetRadiusProcessor;

public class XSmartEdgeClipsProtocolHandler
extends SmartEdgeClipsProtocolHandler
{
@Override
protected void setAgentOptions( RadiusPacket request )
{
RadiusAttribute<byte[]> agentRemoteIdAttribute = request.getAttribute( 2352, 96 );
RadiusAttribute<byte[]> circuitRemoteIdAttribute = request.getAttribute( 2352, 97 );

        byte[] agentRemoteId = agentRemoteIdAttribute.getValue();
        byte[] circuitRemoteId = circuitRemoteIdAttribute.getValue();

        // DLink
        if( agentRemoteId.length == 8 )
        {
            request.setOption( InetRadiusProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 2, 6 ) );
            request.setOption( InetRadiusProcessor.AGENT_CIRCUIT_ID, Arrays.copyOfRange( circuitRemoteId, 2, circuitRemoteId.length - 2 ) );
        }
        else
        {
            request.setOption( InetRadiusProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 5, 6 ) );
            request.setOption( InetRadiusProcessor.AGENT_CIRCUIT_ID, Arrays.copyOfRange( circuitRemoteId, 2, circuitRemoteId.length - 2 ) );
        }
    }
}

Если в DHCP-пакете указан giaddr Cisco/SmartEdge, то в этот класс нужно добавить предобработку DHCP

import java.util.Arrays;

import ru.avantis.abilling.kernel.network.dhcp.DhcpPacket;
import ru.avantis.abilling.kernel.network.radius.RadiusAttribute;
import ru.avantis.abilling.kernel.network.radius.RadiusPacket;
import ru.avantis.abilling.modules.inet.dhcp.InetDhcpProcessor;
import ru.avantis.abilling.modules.inet.radius.InetRadiusProcessor;

public class XSmartEdgeClipsProtocolHandler
extends SmartEdgeClipsProtocolHandler
{
@Override
protected void setAgentOptions( RadiusPacket request )
{
RadiusAttribute<byte[]> agentRemoteIdAttribute = request.getAttribute( 2352, 96 );
RadiusAttribute<byte[]> circuitRemoteIdAttribute = request.getAttribute( 2352, 97 );

        byte[] agentRemoteId = agentRemoteIdAttribute.getValue();
        byte[] circuitRemoteId = circuitRemoteIdAttribute.getValue();

        // DLink
        if( agentRemoteId.length == 8 )
        {
            request.setOption( InetRadiusProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 2, 6 ) );
            request.setOption( InetRadiusProcessor.AGENT_CIRCUIT_ID, Arrays.copyOfRange( circuitRemoteId, 2, circuitRemoteId.length - 2 ) );
        }
        else
        {
            request.setOption( InetRadiusProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 5, 6 ) );
            request.setOption( InetRadiusProcessor.AGENT_CIRCUIT_ID, Arrays.copyOfRange( circuitRemoteId, 2, circuitRemoteId.length - 2 ) );
        }
    }

    @Override
    public void preprocessDhcpRequest( DhcpPacket request, DhcpPacket response )
        throws Exception
    {
        byte[] agentRemoteId = request.getSubOption( (byte)2 ).value;
        // DLink
        if( agentRemoteId.length == 8 )
        {
            request.setOption( InetDhcpProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 2, 6 ) );
        }
        else
        {
            request.setOption( InetDhcpProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 5, 6 ) );
        }
    }
}

Если же giaddr в DHCP-пакете relay agent’а, от которого получил запрос Cisco/SmartEdge, то для типа устройства relay agent’а нужно указать отдельный обработчик, см. выше.