Дерево страниц

Сравнение версий

Ключ

  • Эта строка добавлена.
  • Эта строка удалена.
  • Изменено форматирование.
Примечание
Перед прочтением данной главы рекомендуется к прочтению глава Процессор InetDhcpProcessor.

Оглавление

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


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

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

Для извлечения значения требуется указать код субопции, а также один или два regex - hex и string. hex указывает, что нужно выбрать у значения в опции в 16-ричном формате (один байт - это два символа), string - указывает, что нужно выбрать в значенииопции, преобразованном преобразованной в строку.

Базовые значения

Предположим, у нас в DHCP-пакете присутствует такая option 82:

...

agentRemoteId является вся hexHEX-строка D067B3932607 в субопции 2 - это MAC-адрес, укажем как его извлечь:

...

Таким образом с помощью regex мы указываем, что нам нужны два символа (02) в этой hexHEX-строке.

Для того чтобы получить VLAN, который находится в субопции 1 в первых двух байтах ({076D00020201}), нужно указать:

...

Блок кода
languageruby
dhcp.option82.agentSvlanId.code=1
dhcp.option82.agentSvlanId.hex=^(\w{2})

Логин

Или логин для поиска по логину (для схемы PON, когда в agentRemoteId приходит MAC-адрес ONU):

Блок кода
languageruby
dhcp.option82.login.code=2
dhcp.option82.login.hex=.*

Однако приходящие Извлеченный логин можно сохранить в сессии (сохранение сработает, если включен поиск по логину или логин был установлен в предобработке):

Блок кода
languageruby
dhcp.option82.login.save=1

Опции в виде HEX-строки

Приходящие значения могут представлять собой строку:

...

Блок кода
languageruby
dhcp.option82.interfaceId.code=1
dhcp.option82.interfaceId.hex=^.{4}(.+)$
dhcp.option82.interfaceId.string=port(\d+)

Разные форматы

Regex-ов можно указать несколько - это может быть удобно при наличии устройств разных марок - они могут посылать option 82 в разном формате:

...

При осуществлении настройки проверить ваш Regex вы можете на одном из множества онлайн-сервисов, например, https://regex101.com/. Для преобразования набора байт в 16-ричном представлении в строку также можно воспользоваться онлайн-сервисом.

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

Из RADIUS-пакета необходимо извлечь Agent-Remote-Id и Circuit-Id - значения DHCP option 82. Значения порта/VLAN/SVLAN будут извлечены из Circuit-Id с помощью правил описанных в параметрах dhcp.option82.* (см. выше).

Если приходящие значения закодированы в бинарном формате (например, значение Agent-Remote-Id совпадает с MAC-адресом агентского устройства) и нет префикса с указанием длины опции, то достаточно указать RADIUS-vendor и тип атрибута, например, для Redback Agent-Remote-Id и Agent-Circuit-Id:

Блок кода
languageruby
radius.agentRemoteId.vendor=2352
radius.agentRemoteId.type=96
radius.option82.circuitId.vendor=2352
radius.option82.circuitId.type=97

Если в значениях опций присутствует префикс-длина размером два байта, то его нужно исключить при получении agentRemoteId:

Блок кода
languageruby
radius.agentRemoteId.vendor=2352
radius.agentRemoteId.type=96
radius.agentRemoteId.hex=^.{4}(.*)$
radius.option82.circuitId.vendor=2352
radius.option82.circuitId.type=97

Если в значении закодирована строка, а не бинарные значения, например:

Блок кода
languageruby
Agent-Remote-Id={70 72 61 76 64 79 2D 33 36 61 2D 70 6F 72 74 35 2D 38 30 35 2D 76 6C 61 6E}
Agent-Circuit-Id={34 36 2D 6C 65 73 6F 7A 61 76 6F 64 20 65 74 68 20 30 2F 31 32 35}

то в конфигурации указываем преобразование в строку:

Блок кода
languageruby
radius.agentRemoteId.vendor=2352
radius.agentRemoteId.type=96
radius.agentRemoteId.string=.*
radius.option82.circuitId.vendor=2352
radius.option82.circuitId.type=97

Circuit-Id в двух последних случаях мы извлекаем как есть - подразумевается, что извлеченное значение будет идентично значению DHCP-опции в DHCP-пакете и его обработка уже прописана в параметрах dhcp.option82.*.

Для более сложных вариантов возможно извлечение с помощью предобработки (см. далее).

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

...

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

Пример совмещения предобработки с конфигурацией (PON)

В данном примере из пакета извлекаются нужные значения, устанавливаются в запрос, а также формируется строка, которая далее окажется в поле "На номер" сессии (InetDhcpProcessor.CALLED_STATION_ID). Данную строку можно будет использовать как информационную (она отображается в клиенте биллинга), а также первом подключении (регистрации) абонента для заполнения полей сервиса договора.

Конфигурация:

Блок кода
languageruby
# Ищем по "логину"
dhcp.servSearchMode=0

# Извлечение логина из запроса - MAC-адрес или идентификатор PON
dhcp.option82.login.code=2
dhcp.option82.login.hex=.*
# сохраняем "логин" в сессии
dhcp.option82.login.save=1

# Не извлекаем agentRemoteId как идентификатор устройства (мы используем его как логин)
dhcp.option82.agentRemoteId.code=0
# Извлечение SVLAN
dhcp.option82.agentSvlanId.code=1
dhcp.option82.agentSvlanId.hex=^(\w{4})

# Данные параметры извлекаются в предобработке
# порт OLT
dhcp.option82.oltPort.code=1
dhcp.option82.oltPort.hex=^.{6}(\w{2})
# ont ID на порту OLT
dhcp.option82.ontId.code=1
dhcp.option82.ontId.hex=^.{8}(\w{2})

Предобработка:

Блок кода
languagejava
collapsetrue
package ru.provider.bgbilling.modules.inet.dyn.device;

import javax.annotation.Resource;

import org.apache.log4j.Logger;

import ru.bitel.bgbilling.kernel.network.dhcp.DhcpOption;
import ru.bitel.bgbilling.kernel.network.dhcp.DhcpPacket;
import ru.bitel.bgbilling.modules.inet.access.sa.ProtocolHandlerAdapter;
import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDevice;
import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDeviceType;
import ru.bitel.bgbilling.modules.inet.dhcp.InetDhcpProcessor;
import ru.bitel.bgbilling.modules.inet.runtime.device.PacketValueExtractor;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.io.BinaryValueExtractor;

public class PonDhcpRelayProtocolHandler
    extends ProtocolHandlerAdapter
{
    private static final Logger logger = Logger.getLogger( PonDhcpRelayProtocolHandler.class );

    @Resource
    private PacketValueExtractor packetValueExtractor;

    private byte oltPortCode;
    private BinaryValueExtractor oltPortExtractor;

    private byte ontIdPortCode;
    private BinaryValueExtractor ontIdPortExtractor;

    @Override
    public void init( Setup setup, int moduleId, InetDevice inetDevice, InetDeviceType inetDeviceType, ParameterMap deviceConfig )
        throws Exception
    {
        oltPortCode = (byte)deviceConfig.getInt( "dhcp.option82.oltPort.code", 1 );
        oltPortExtractor = packetValueExtractor.getExtractor( deviceConfig, "dhcp.option82.oltPort." );

        ontIdPortCode = (byte)deviceConfig.getInt( "dhcp.option82.ontId.code", 1 );
        ontIdPortExtractor = packetValueExtractor.getExtractor( deviceConfig, "dhcp.option82.ontId." );
    }

    @Override
    public void preprocessDhcpRequest( final DhcpPacket request, final DhcpPacket response )
        throws Exception
    {
        final int svlan = packetValueExtractor.getOption82AgentSvlanId( request, true );
        final int vlan = packetValueExtractor.getOption82VlanId( request, true );

        final int oltPort = extract( request, oltPortCode, oltPortExtractor );
        final int ontId = extract( request, ontIdPortCode, ontIdPortExtractor );

        final String onuIdentifier = (String)packetValueExtractor.getOption82Login( request, true );

        final String calledStationId = svlan + "." + vlan + "/" + oltPort + ":" + ontId + "/" + onuIdentifier;

        logger.info( calledStationId );

        // устанавливаем VLAN, чтобы он был досупен биллингу при обработке пакета
        request.setOption( InetDhcpProcessor.VLAN_ID, vlan );
        // устанавливаем VLAN в circuitId сессии
        request.setOption( InetDhcpProcessor.CIRCUIT_ID, vlan );
        // устанавливаем в calledStationId сессии, для информации
        request.setOption( InetDhcpProcessor.CALLED_STATION_ID, calledStationId );
    }

    private static int extract( final DhcpPacket request, final byte code, final BinaryValueExtractor extractor )
    {
        final DhcpOption subOption = request.getSubOption( code );
        if( subOption != null )
        {
            return extractor.extractInt( subOption.value, -1 );
        }

        return -1;
    }
}