pppoe源码分析
2017-08-21 14:54
155 查看
pppoe源码分析
发现阶段
1 PADIactive discovery initiation
2 PADO active discovery offer
3 RADR active discovery request
4 PADS active discovery session-confirmation
5 PADT
会话阶段
关键数据结构
host ———–PADI—————–> server
host <———–PADO—————– server
host ———–PADR—————–> server
host <———–PADS—————– server
2.CODE(代码)字段值为0×09(PADI Code),SESSION-ID(会话ID)字段值为0x0000。
生成hostUniq
3.PADI分组必须至少包含一个服务名称类型的标签(Service Name Tag,字段值为0x0101),向接入集中器提出所要求提供的服务。
服务器:pppoe-server.c processPADI
可接受的tag:
2.其中CODE字段值为0×07(PADO Code),SESSION-ID字段值仍为0x0000。
3.PADO分组必须包含一个接入集中器名称类型的标签(Access Concentrator Name Tag,字段值为0x0102),以及一个或多个服务名称类型标签,表明可向主机提供的服务种类。
1.其中CODE字段为0x19(PADR Code),SESSION_ID字段值仍为0x0000。
2.PADR分组必须包含一个服务名称类型标签,确定向接入集线器(或交换机)请求的服务种类。
3.当主机在指定的时间内没有接收到PADO,它应该重新发送它的PADI分组,并且加倍等待时间,这个过程会被重复期望的次数。
2.接入集中器收到PADR分组后准备开始PPP会话,它发送一个PPPoE有效发现会话确认PADS分组。
其中CODE字段值为0×65(PADS Code),SESSION-ID字段值为接入集中器所产生的一个惟一的PPPoE会话标识号码。
4.PADS和PADR的Host-Uniq Tag值相同。
tags:
Host-Uniq
为主机唯一标识,类似于PPP数据报文中的标识域,主要是用来匹配发送和接收端的。因为对于 广播式的网络中会同时存在很多个PPPoE的数据报文。
AC-Cookie
Ac-Cookie是为了防止拒绝服务攻击(Denial of Service,简称DOS)。访问集中器(AC)能够根据PADR的源地址来重新产生唯一的Tag_Value。使用这种方法,AC可以确保PADI的 源地址是可达的,并对该地址的并行会话数进行限制。
用户主机与接入集中器根据在发现阶段所协商的PPP会话连接参数进行PPP会话。一旦PPPoE会话开始,PPP数据就可以以任何其他的PPP封装形式发送。所有的以太网帧都是单播的。PPPoE会话的SESSION-ID一定不能改变,并且必须是发现阶段分配的值。
发现阶段
1 PADIactive discovery initiation
2 PADO active discovery offer
3 RADR active discovery request
4 PADS active discovery session-confirmation
5 PADT
会话阶段
关键数据结构
1. 发现阶段
发现阶段是无状态的,目的是获得PPPoE终结端(在局端的ADSL设备上)的以太网MAC地址,并建立一个惟一的PPPoE SESSION-ID。host ———–PADI—————–> server
host <———–PADO—————– server
host ———–PADR—————–> server
host <———–PADS—————– server
1.1 PADI,active discovery initiation
1.主机广播发起分组,分组的目的地址为以太网的广播地址 0xffffffffffff/* Set destination to Ethernet broadcast address */ memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN);
Ethernet II, Src: Dell_49:3f:5f (24:b6:fd:49:3f:5f), Dst: Broadcast (ff:ff:ff:ff:ff:ff) Destination: Broadcast (ff:ff:ff:ff:ff:ff) Source: Dell_49:3f:5f (24:b6:fd:49:3f:5f) Type: PPPoE Discovery (0x8863)
2.CODE(代码)字段值为0×09(PADI Code),SESSION-ID(会话ID)字段值为0x0000。
packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); //#define ETH_PPPOE_DISCOVERY 0x8863 packet.vertype = PPPOE_VER_TYPE(1, 1); //#define PPPOE_VER_TYPE(v, t) (((v) << 4) | (t)) packet.code = CODE_PADI; //#define CODE_PADI 0x09 packet.session = 0;
生成hostUniq
/* If we're using Host-Uniq, copy it over */ if (conn->hostUniq) { PPPoETag hostUniq; int len = (int) strlen(conn->hostUniq); hostUniq.type = htons(TAG_HOST_UNIQ); hostUniq.length = htons(len); memcpy(hostUniq.payload, conn->hostUniq, len); CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); memcpy(cursor, &hostUniq, len + TAG_HDR_SIZE); cursor += len + TAG_HDR_SIZE; plen += len + TAG_HDR_SIZE; }
PPP-over-Ethernet Discovery 0001 .... = Version: 1 .... 0001 = Type: 1 Code: Active Discovery Initiation (PADI) (0x09) Session ID: 0x0000 Payload Length: 20 PPPoE Tags Host-Uniq: 010000000000000001000000
3.PADI分组必须至少包含一个服务名称类型的标签(Service Name Tag,字段值为0x0101),向接入集中器提出所要求提供的服务。
if (!omit_service_name) { plen = TAG_HDR_SIZE + namelen; CHECK_ROOM(cursor, packet.payload, plen); svc->type = TAG_SERVICE_NAME; svc->length = htons(namelen); if (conn->serviceName) { memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); } cursor += namelen + TAG_HDR_SIZE; } else{ plen = 0; }
1.2 PADO, active discovery offer
1.接入集中器收到在服务范围内的PADI分组,发送PPPoE有效发现提供包分组,以响应请求。服务器:pppoe-server.c processPADI
可接受的tag:
PPPoETag acname; PPPoETag servname; PPPoETag cookie;
2.其中CODE字段值为0×07(PADO Code),SESSION-ID字段值仍为0x0000。
PPP-over-Ethernet Discovery 0001 .... = Version: 1 .... 0001 = Type: 1 Code: Active Discovery Offer (PADO) (0x07) Session ID: 0x0000 Payload Length: 50 PPPoE Tags Host-Uniq: 010000000000000001000000 AC-Name: SH-SH-SJCX-BAS-1.MAN.ME60X
/* Generate a cookie */ cookie.type = htons(TAG_AC_COOKIE); cookie.length = htons(COOKIE_LEN); genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookie.payload); /********************************************************************** *%FUNCTION: genCookie *%ARGUMENTS: * peerEthAddr -- peer Ethernet address (6 bytes) * myEthAddr -- my Ethernet address (6 bytes) * seed -- random cookie seed to make things tasty (16 bytes) * cookie -- buffer which is filled with server PID and * md5 sum of previous items *%RETURNS: * Nothing *%DESCRIPTION: * Forms the md5 sum of peer MAC address, our MAC address and seed, useful * in a PPPoE Cookie tag. ***********************************************************************/
/* Construct a PADO packet */ //包头 memcpy(pado.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN); memcpy(pado.ethHdr.h_source, myAddr, ETH_ALEN); pado.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); pado.ver = 1; pado.type = 1; pado.code = CODE_PADO; pado.session = 0;
3.PADO分组必须包含一个接入集中器名称类型的标签(Access Concentrator Name Tag,字段值为0x0102),以及一个或多个服务名称类型标签,表明可向主机提供的服务种类。
plen = TAG_HDR_SIZE + acname_len; CHECK_ROOM(cursor, pado.payload, acname_len+TAG_HDR_SIZE); memcpy(cursor, &acname, acname_len + TAG_HDR_SIZE); cursor += acname_len + TAG_HDR_SIZE; /* If we asked for an MTU, handle it */ if (max_ppp_payload > ETH_PPPOE_MTU && ethif->mtu > 0) { /* Shrink payload to fit */ if (max_ppp_payload > ethif->mtu - TOTAL_OVERHEAD) { max_ppp_payload = ethif->mtu - TOTAL_OVERHEAD; } if (max_ppp_payload > ETH_JUMBO_LEN - TOTAL_OVERHEAD) { max_ppp_payload = ETH_JUMBO_LEN - TOTAL_OVERHEAD; } if (max_ppp_payload > ETH_PPPOE_MTU) { PPPoETag maxPayload; UINT16_t mru = htons(max_ppp_payload); maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); maxPayload.length = htons(sizeof(mru)); memcpy(maxPayload.payload, &mru, sizeof(mru)); CHECK_ROOM(cursor, pado.payload, sizeof(mru) + TAG_HDR_SIZE); memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); cursor += sizeof(mru) + TAG_HDR_SIZE; plen += sizeof(mru) + TAG_HDR_SIZE; } } /* If no service-names specified on command-line, just send default zero-length name. Otherwise, add all service-name tags */ servname.type = htons(TAG_SERVICE_NAME); if (!NumServiceNames) { servname.length = 0; CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE); memcpy(cursor, &servname, TAG_HDR_SIZE); cursor += TAG_HDR_SIZE; plen += TAG_HDR_SIZE; } else { for (i=0; i<NumServiceNames; i++) { int slen = strlen(ServiceNames[i]); servname.length = htons(slen); CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE+slen); memcpy(cursor, &servname, TAG_HDR_SIZE); memcpy(cursor+TAG_HDR_SIZE, ServiceNames[i], slen); cursor += TAG_HDR_SIZE+slen; plen += TAG_HDR_SIZE+slen; } } CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE + COOKIE_LEN); memcpy(cursor, &cookie, TAG_HDR_SIZE + COOKIE_LEN); cursor += TAG_HDR_SIZE + COOKIE_LEN; plen += TAG_HDR_SIZE + COOKIE_LEN; if (relayId.type) { CHECK_ROOM(cursor, pado.payload, ntohs(relayId.length) + TAG_HDR_SIZE); memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); cursor += ntohs(relayId.length) + TAG_HDR_SIZE; plen += ntohs(relayId.length) + TAG_HDR_SIZE; } if (hostUniq.type) { CHECK_ROOM(cursor, pado.payload, ntohs(hostUniq.length)+TAG_HDR_SIZE); memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; }
1.3 RADR, active discovery request
主机在可能收到的多个PADO分组中选择一个合适的PADO分组,然后向所选择的接入集中器发送PPPoE有效发现请求分组。1.其中CODE字段为0x19(PADR Code),SESSION_ID字段值仍为0x0000。
memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); packet.vertype = PPPOE_VER_TYPE(1, 1); packet.code = CODE_PADR; packet.session = 0;
PPP-over-Ethernet Discovery 0001 .... = Version: 1 .... 0001 = Type: 1 Code: Active Discovery Request (PADR) (0x19) Session ID: 0x0000 Payload Length: 20 PPPoE Tags Host-Uniq: 010000000000000002000000
2.PADR分组必须包含一个服务名称类型标签,确定向接入集线器(或交换机)请求的服务种类。
svc->type = TAG_SERVICE_NAME; svc->length = htons(namelen); if (conn->serviceName) { memcpy(svc->payload, conn->serviceName, namelen); } cursor += namelen + TAG_HDR_SIZE;
3.当主机在指定的时间内没有接收到PADO,它应该重新发送它的PADI分组,并且加倍等待时间,这个过程会被重复期望的次数。
do { padiAttempts++; if (padiAttempts > MAX_PADI_ATTEMPTS) { warn("Timeout waiting for PADO packets"); close(conn->discoverySocket); conn->discoverySocket = -1; return; } sendPADI(conn); conn->discoveryState = STATE_SENT_PADI; waitForPADO(conn, timeout); } while (conn->discoveryState == STATE_SENT_PADI);
1.4 PADS, active discovery session-confirmation
1.产生了唯一的sessionid2.接入集中器收到PADR分组后准备开始PPP会话,它发送一个PPPoE有效发现会话确认PADS分组。
其中CODE字段值为0×65(PADS Code),SESSION-ID字段值为接入集中器所产生的一个惟一的PPPoE会话标识号码。
/* Send PADS and Start pppd */ memcpy(pads.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN); memcpy(pads.ethHdr.h_source, myAddr, ETH_ALEN); pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); pads.ver = 1; pads.type = 1; pads.code = CODE_PADS; //#define CODE_PADS 0x65 pads.session = cliSession->sess;
PPP-over-Ethernet Discovery 0001 .... = Version: 1 .... 0001 = Type: 1 Code: Active Discovery Session-confirmation (PADS) (0x65) Session ID: 0x3f43 Payload Length: 20 PPPoE Tags Host-Uniq: 010000000000000002000000
4.PADS和PADR的Host-Uniq Tag值相同。
1.5 PADT
它可以在会话建立后的任何时候发送,来终止PPPoE会话,也就是会话释放。它可以由主机或者接入集中器发送。当对方接收到一个PADT分组,就不再允许使用这个会话来发送PPP业务。PADT分组不需要任何标签,其CODE字段值为0×a7,SESSION-ID字段值为需要终止的PPP会话的会话标识号码。在发送或接收PADT后,即使正常的PPP终止分组也不必发送。PPP对端应该使用PPP协议自身来终止PPPoE会话,但是当PPP不能使用时,可以使用PADT。tags:
Host-Uniq
为主机唯一标识,类似于PPP数据报文中的标识域,主要是用来匹配发送和接收端的。因为对于 广播式的网络中会同时存在很多个PPPoE的数据报文。
AC-Cookie
Ac-Cookie是为了防止拒绝服务攻击(Denial of Service,简称DOS)。访问集中器(AC)能够根据PADR的源地址来重新产生唯一的Tag_Value。使用这种方法,AC可以确保PADI的 源地址是可达的,并对该地址的并行会话数进行限制。
2. 会话阶段
LCP协商 –》 认证 –》NCP协商用户主机与接入集中器根据在发现阶段所协商的PPP会话连接参数进行PPP会话。一旦PPPoE会话开始,PPP数据就可以以任何其他的PPP封装形式发送。所有的以太网帧都是单播的。PPPoE会话的SESSION-ID一定不能改变,并且必须是发现阶段分配的值。
3. 关键数据结构
pppoe包结构 /* A PPPoE Packet, including Ethernet headers */ typedef struct PPPoEPacketStruct { struct ethhdr ethHdr; /* Ethernet header */ unsigned int vertype:8; /* PPPoE Version and Type (must both be 1) */ unsigned int code:8; /* PPPoE code */ unsigned int session:16; /* PPPoE session */ unsigned int length:16; /* Payload length */ unsigned char payload[ETH_JUMBO_LEN]; /* A bit of room to spare */ } PPPoEPacket;
/* Header size of a PPPoE packet */ #define PPPOE_OVERHEAD 6 /* type, code, session, length */ #define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD) #define MAX_PPPOE_PAYLOAD (ETH_JUMBO_LEN - PPPOE_OVERHEAD) /* There are other fixed-size buffers preventing this from being increased to 16110. The buffer sizes would need to be properly de-coupled from the default MRU. For now, getting up to 1500 is enough. */ //#define ETH_JUMBO_LEN 1508 #define PPP_OVERHEAD 2 /* protocol */ #define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - PPP_OVERHEAD) #define TOTAL_OVERHEAD (PPPOE_OVERHEAD + PPP_OVERHEAD) #define ETH_PPPOE_MTU (ETH_DATA_LEN - TOTAL_OVERHEAD) //#define ETH_DATA_LEN ETHERMTU
/* PPPoE Tag */ typedef struct PPPoETagStruct { unsigned int type:16; /* tag type */ unsigned int length:16; /* Length of payload */ unsigned char payload[ETH_JUMBO_LEN]; /* A LOT of room to spare */ } PPPoETag; /* PPPoE Tags */ #define TAG_END_OF_LIST 0x0000 #define TAG_SERVICE_NAME 0x0101 #define TAG_AC_NAME 0x0102 #define TAG_HOST_UNIQ 0x0103 #define TAG_AC_COOKIE 0x0104 #define TAG_VENDOR_SPECIFIC 0x0105 #define TAG_RELAY_SESSION_ID 0x0110 #define TAG_PPP_MAX_PAYLOAD 0x0120 #define TAG_SERVICE_NAME_ERROR 0x0201 #define TAG_AC_SYSTEM_ERROR 0x0202 #define TAG_GENERIC_ERROR 0x0203
/* A client session */ typedef struct ClientSessionStruct { struct ClientSessionStruct *next; /* In list of free or active sessions */ PppoeSessionFunctionTable *funcs; /* Function table */ pid_t pid; /* PID of child handling session */ Interface *ethif; /* Ethernet interface */ unsigned char myip[IPV4ALEN]; /* Local IP address */ unsigned char peerip[IPV4ALEN]; /* Desired IP address of peer */ UINT16_t sess; /* Session number */ unsigned char eth[ETH_ALEN]; /* Peer's Ethernet address */ unsigned int flags; /* Various flags */ time_t startTime; /* When session started */ char const *serviceName; /* Service name */ UINT16_t requested_mtu; /* Requested PPP_MAX_PAYLOAD per RFC 4638 */ #ifdef HAVE_LICENSE char user[MAX_USERNAME_LEN+1]; /* Authenticated user-name */ char realm[MAX_USERNAME_LEN+1]; /* Realm */ unsigned char realpeerip[IPV4ALEN]; /* Actual IP address -- may be assigned by RADIUS server */ int maxSessionsPerUser; /* Max sessions for this user */ #endif #ifdef HAVE_L2TP l2tp_session *l2tp_ses; /* L2TP session */ struct sockaddr_in tunnel_endpoint; /* L2TP endpoint */ #endif } ClientSession;
typedef struct PPPoEConnectionStruct { int discoveryState; /* Where we are in discovery */ int discoverySocket; /* Raw socket for discovery frames */ int sessionSocket; /* Raw socket for session frames */ unsigned char myEth[ETH_ALEN]; /* My MAC address */ unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */ unsigned char req_peer_mac[ETH_ALEN]; /* required peer MAC address */ unsigned char req_peer; /* require mac addr to match req_peer_mac */ UINT16_t session; /* Session ID */ char *ifName; /* Interface name */ char *serviceName; /* Desired service name, if any */ char *acName; /* Desired AC name, if any */ int synchronous; /* Use synchronous PPP */ int useHostUniq; /* Use Host-Uniq tag */ int printACNames; /* Just print AC names */ FILE *debugFile; /* Debug file for dumping packets */ int numPADOs; /* Number of PADO packets received */ PPPoETag cookie; /* We have to send this if we get it */ PPPoETag relayId; /* Ditto */ int error; /* Error packet received */ int debug; /* Set to log packets sent and received */ int discoveryTimeout; /* Timeout for discovery packets */ int seenMaxPayload; int mtu; /* Stored MTU */ int mru; /* Stored MRU */ } PPPoEConnection;
相关文章推荐
- PPPOE源码分析
- PPPOE源码分析
- [PPPOE]RP-PPPOE源码分析
- PPPOE源码分析
- [PPPOE]PPPD源码分析
- nginx源码分析之ngx_list_t
- Volley源码分析(三)
- mosquitto源码分析(六)
- FastDFS-Nginx扩展模块源码分析 -- mod_fastdfs原理解析
- HTTP严格安全传输(HTTP Strict Transport Security, HSTS)chromuim实现源码分析(二)
- SpringMVC源码分析(3)DispatcherServlet的请求处理流程
- 【精】OkHttp3源码分析[综述]
- yii源码分析3
- Opencv2.4.9源码分析——Stitching(五)
- red5源码分析---8
- Android电话拨打流程源码分析
- Sizzle源码分析:一 设计思路
- Java和Android的Thread源码分析
- kubelet源码分析(version: git tag 1.7.6)
- tornado框架源码分析---Application类之debug参数