Примечание |
---|
Перед прочтением данной главы рекомендуется к прочтению глава Процессор 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}), нужно указать:
...
Блок кода | ||
---|---|---|
| ||
dhcp.option82.agentSvlanId.code=1 dhcp.option82.agentSvlanId.hex=^(\w{2}) |
Логин
Или логин для поиска по логину (для схемы PON, когда в agentRemoteId приходит MAC-адрес ONU):
Блок кода | ||
---|---|---|
| ||
dhcp.option82.login.code=2 dhcp.option82.login.hex=.* |
Однако приходящие Извлеченный логин можно сохранить в сессии (сохранение сработает, если включен поиск по логину или логин был установлен в предобработке):
Блок кода | ||
---|---|---|
| ||
dhcp.option82.login.save=1 |
Опции в виде HEX-строки
Приходящие значения могут представлять собой строку:
...
Блок кода | ||
---|---|---|
| ||
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:
Блок кода | ||
---|---|---|
| ||
radius.agentRemoteId.vendor=2352
radius.agentRemoteId.type=96
radius.option82.circuitId.vendor=2352
radius.option82.circuitId.type=97 |
Если в значениях опций присутствует префикс-длина размером два байта, то его нужно исключить при получении agentRemoteId:
Блок кода | ||
---|---|---|
| ||
radius.agentRemoteId.vendor=2352
radius.agentRemoteId.type=96
radius.agentRemoteId.hex=^.{4}(.*)$
radius.option82.circuitId.vendor=2352
radius.option82.circuitId.type=97 |
Если в значении закодирована строка, а не бинарные значения, например:
Блок кода | ||
---|---|---|
| ||
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} |
то в конфигурации указываем преобразование в строку:
Блок кода | ||
---|---|---|
| ||
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 указанием позиции и длины
...
Блок кода | ||
---|---|---|
| ||
import java.util.Arrays; import ru.bitel.bgbilling.kernel.network.dhcp.DhcpPacket; import ru.bitel.bgbilling.kernel.network.dhcp.DhcpProtocolHandler; import ru.bitel.bgbilling.modules.inet.access.sa.ProtocolHandlerAdapter; import ru.bitel.bgbilling.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-пакета.
...
Если же giaddr в DHCP-пакете relay agent'а, от которого получил запрос Cisco/SmartEdge, то для типа устройства relay agent'а нужно указать отдельный обработчик, см. выше.
Пример совмещения предобработки с конфигурацией (PON)
В данном примере из пакета извлекаются нужные значения, устанавливаются в запрос, а также формируется строка, которая далее окажется в поле "На номер" сессии (InetDhcpProcessor.CALLED_STATION_ID). Данную строку можно будет использовать как информационную (она отображается в клиенте биллинга), а также первом подключении (регистрации) абонента для заполнения полей сервиса договора.
Конфигурация:
Блок кода | ||
---|---|---|
| ||
# Ищем по "логину"
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}) |
Предобработка:
Блок кода | ||||
---|---|---|---|---|
| ||||
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;
}
} |