PPPoE协议发现阶段交互及FPGA实现过程

发现阶段交互过程

PPPoE ( Point to Point Protocol over Ethernet ),基于以太网的点对点协议,包括发现阶段和会话阶段。发现阶段,即 PPPoE Discovery,目的是获取客户端MAC地址并建立连接。发现阶段包括PADI、PADO、PADR、PADS四个阶段。发现阶段结束后,就进入标准PPP会话阶段。发现阶段交互过程如下图所示:

图1 PPPoE发现阶段交互过程

数据帧格式如下图所示:

图2 PPPoE数据帧格式

帧类型域发现阶段都是0x8863,会话阶段都是0x8864;版本和类型在发现阶段和会话阶段都是0x01,代码域不同阶段对应着不同的类型号;会话ID默认为0x0000,在服务器向客户端发送PADS中会指定一个唯一标识会话ID;长度指的是静载荷的长度,单位是字节B。

PADI

PADI ( PPPoE Active Discovery Initiation ),PADI是广播帧,用于寻找可用的服务器,由客户端发送。PADI分组必须包含至少一个服务类型标签 (Service Name Tag,字段值为0x0101),向服务器提出所要求的提供的服务。当客户端在一定时间内没有收到PADO,客户端会重新发送PADI,并加倍等待时间。
目的地址:0xFFFF_FFFF
帧类型域:0x8863
代码域:0x09
会话ID:0x0000
Wireshark接收到的PADI如下图所示:

图3 Wireshark接收的PADI数据帧

没有画圈的字节就是静载荷。当接收到PADI帧,就可以根据数据帧格式提取出客户端MAC地址、帧类型、代码ID、载荷帧长

//============================================
// 提取客户端MAC地址、帧类型、代码ID、载荷帧长
//============================================
always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0) begin
        cilent_addr <= 48'b0;
    end
    else begin
        if (cnt == 4'd2 && n_state == PADI) begin
            cilent_addr[47:32] <= rx_data_ff[15:0];
        end
        else if (cnt == 4'd3 && n_state == PADI) begin
            cilent_addr[31:0] <= rx_data_ff;
        end
        else begin
            cilent_addr <= cilent_addr;
        end
    end
end

always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0) begin
        fram_type <= 16'b0;
        code_id <= 8'b0;
        rx_fram_len <= 16'b0; 
    end
    else begin
        if (cnt == 4'd4) begin
            fram_type <= rx_data_ff[31:16];
            code_id <= rx_data_ff[7:0];
        end
        else if (cnt == 4'd5)
            rx_fram_len <= rx_data_ff[15:0];
        else begin
            fram_type <= fram_type;
            code_id <= code_id;
            rx_fram_len <= rx_fram_len;
        end
    end
end
//============================================

载荷要用FIFO存起来,因为PADO载荷是由PADI的Host-Uniq Tag和AC_name(服务器名字)组成的

//============================================
//                fifo读、写
//============================================
always@(*)
begin
    if((cnt_PADO == 5'd3) || (cnt_PADS == 5'd3)) fifo_ren <= 1'b1;
    else if(fifo_empty == 1'b1) fifo_ren <= 1'b0;
    else fifo_ren <= fifo_ren;
end

always@(*)//头部发送完毕而且fifo计数<=载荷帧长,写
begin     //写数据条件一定要严格,因为网络会定时发送无用帧来保持链接,不能把无用帧的载荷存到fifo里面,否则下次读的时候会把无用帧的载荷也读出来加到PADO或者PADS中
        if (cnt > 4'd5 && ((c_state == PADI && code_id == `PADI_CODE_ID) || (c_state == PADR && code_id == `PADR_CODE_ID)) && rx_dval_ff == 1'b1 && (fram_type == 16'h8863) && ((cnt_fifo << 2) <= rx_fram_len )) fifo_wren = 1'b1;
        else fifo_wren = 1'b0;
end
//============================================

sync_fifo #(
    .WIDTH (32),
    .ADDR (6)
) U_sync_fifo(
    .clk   (clk         ),
    .rst_n (rst_n       ), 
    .din   (rx_data_ff  ),
    .wr_en (fifo_wren   ), 
    .full  (fifo_full   ), 
    .dout  (fifo_dout   ),
    .rd_en (fifo_ren    ), 
    .empty (fifo_empty)
);

PADO

PADO ( PPPoE Active Discovery Offer),当服务器接收到PADI帧,服务器就发送PADO帧响应请求。PADO帧也必须包含至少一个服务器名称类型标签(字段值为0x0102),表明可向主机提供的服务种类。PADO载荷是由PADI的Host-Uniq Tag和AC_name(服务器名字)组成。
目的地址:提取的客户端MAC地址
帧类型域:0x8863
代码域:0x07
会话ID:0x0000
Wireshark接收到服务器发送的的PADO如下图所示:

图4 Wireshark接收到的PADO数据帧

最后圈中的4字节数据不是PADI的Host-Uniq Tag,也不是AC_name,是PADO的服务器名称类型标签,0x000d表示名称字节数量。当提取出PADI或者PADR的帧长时,就可得出PADO和PADS的帧头部

//============================================
//              事先算好帧头
//============================================
always@(posedge clk or negedge rst_n)
begin
    if (rst_n == 1'b0) begin
        fram_head_PADO <= 160'b0;
        fram_head_PADS <= 160'b0;
        ac_name        <= 150'b0;
    end
    else begin
        fram_head_PADO <= {cilent_addr, `SERVER_ADDRE, `FRAM_TYPE_FIND, `EDITION_TYPE, `PADO_CODE_ID, `SESSION_ID, {rx_fram_len + 16'h11}};
        fram_head_PADS <= {cilent_addr, `SERVER_ADDRE, `FRAM_TYPE_FIND, `EDITION_TYPE, `PADS_CODE_ID, 16'habcd, rx_fram_len};
        ac_name        <= `AC_NAME;
    end
end
//============================================

PADO帧长+16‘h11是因为还有AC_name和服务器名称类型标签。

PADR

PADR ( PPPoE Active Discovery Request ),客户端在收到的一个或多个PADO中选择一个,然后向选择的服务器发送PADR。PADR也必须包含一个服务名称类型标签。
目的地址:服务器MAC地址
帧类型域:0x8863
代码域:0x19
会话ID:0x0000
Wireshark接收到服务器发送的的PADO如下图所示:

图5 Wireshark接收到的PADR数据帧

PADR的接收流程与PADI的接收流程一样,提取客户端MAC地址、帧类型、代码ID、载荷帧长,并将载荷存在FIFO中。

PADS

PADS ( PPPoE Active Discovery Session-confirmation ),服务器收到PADR后,组建PADS帧。PADS和PADR的Host-Uniq Tag值相同。
目的地址:客户端MAC地址
帧类型域:0x8863
代码域:0x65
会话ID:唯一标识会话ID的值,不能是0x0000
Wireshark接收到服务器发送的的PADO如下图所示:

图6 Wireshark接收到的PADS数据帧

Host-Uniq Tag后面的数据并不需要发,是传输时自动加上的。发送PADO和PADS需要计数器进行计数,以此来判别什么时候发帧头,什么时候发载荷,什么时候发AC_name

//============================================
//          PADO、PADS、FIFO计数
//============================================
always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0)
        cnt_PADO <= 5'b0;
    else begin
        if ((n_state == PADO) && (cnt_PADO == 5'd6) && (fifo_empty_ff == 1'b1)) cnt_PADO <= cnt_PADO + 1'b1;//fifo空了,输出AC_name
        else if ((n_state == PADO) && (cnt_PADO == 5'd6)) cnt_PADO <= 5'd6;//读fifo
        else if((n_state == PADO) && (tx_eop == 1'b1)) cnt_PADO <= 5'b0;//发送结束,清零
        else if (n_state == PADO) cnt_PADO <= cnt_PADO + 1'b1;//发送帧头
        else cnt_PADO <= 5'b0;
    end
end

always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0)
        cnt_PADS <= 5'b0;
    else begin
        if ((n_state == PADS) && (cnt_PADS == 5'd6)) cnt_PADS <= 5'd6;//读fifo
        else if((n_state == PADS) && (tx_eop == 1'b1)) cnt_PADS <= 5'b0;//发送结束清零
        else if (n_state == PADS) cnt_PADS <= cnt_PADS + 1'b1;//发送帧头
        else cnt_PADS <= 5'b0;
    end
end

always@(posedge clk or negedge rst_n)
begin
    if (rst_n == 1'b0) cnt_fifo <= 4'b0;
    else if (cnt >= 4'd5) cnt_fifo <= cnt_fifo + 1'b1;//帧头发送完毕,发送fifo
    else cnt_fifo <= 4'b0;
end
//============================================

根据计数值判断发送哪些数据

//============================================
//              PADO、PADS发送
//============================================
always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0) begin
        tx_data <= 32'b1;  
    end
    else begin
        if (c_state == PADO) begin
            case (cnt_PADO)
                5'b1: tx_data <= fram_head_PADO[159:128];
                5'd2: tx_data <= fram_head_PADO[127:96];
                5'd3: tx_data <= fram_head_PADO[95:64];
                5'd4: tx_data <= fram_head_PADO[63:32];
                5'd5: tx_data <= fram_head_PADO[31:0];
                5'd6: tx_data <= fifo_dout_ff;//发送载荷
                5'd7: tx_data <= ac_name[159:128];//发送AC_NAME
                5'd8: tx_data <= ac_name[127:96];
                5'd9: tx_data <= ac_name[95:64];
                5'd10: tx_data <= ac_name[63:32];
                5'd11: tx_data <= ac_name[31:0];
                default: tx_data <= 32'h2;
            endcase
        end
        else if (c_state == PADS) begin
            case (cnt_PADS)
                5'b1: tx_data <= fram_head_PADS[159:128];
                5'd2: tx_data <= fram_head_PADS[127:96];
                5'd3: tx_data <= fram_head_PADS[95:64];
                5'd4: tx_data <= fram_head_PADS[63:32];
                5'd5: tx_data <= fram_head_PADS[31:0];
                5'd6: tx_data <= fifo_dout_ff;//发送载荷
                default: tx_data <= 32'h3;
            endcase
        end
        else tx_data <= tx_data;
    end
end

always@(posedge clk or negedge rst_n)
begin
   if(rst_n == 1'b0) begin
       tx_mod  <= 2'b0 ;
       tx_dval <= 1'b0 ;
       tx_sop  <= 1'b0 ;
       tx_eop  <= 1'b0 ;
   end 
   else begin
        if (c_state == PADO)
            case (cnt_PADO)
                5'b1: {tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b1, 1'b1, 1'b0};
                5'd2: {tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b1, 1'b0, 1'b0};
                5'd11:{tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b01, 1'b1, 1'b0, 1'b1};
                5'd12:{tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b0, 1'b0, 1'b0};
                default: {tx_mod, tx_dval, tx_sop, tx_eop} <= {tx_mod, tx_dval, tx_sop, tx_eop};
            endcase
        else if (c_state == PADS) begin
            if(cnt_PADS == 5'b1) {tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b1, 1'b1, 1'b0};
            else if (cnt_PADS == 5'd2) {tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b1, 1'b0, 1'b0};
            else if (fifo_ren_n == 1'b1) {tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b1, 1'b0, 1'b1};
            else if (fifo_ren_nn == 1'b1) {tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b0, 1'b0, 1'b0};
            else  {tx_mod, tx_dval, tx_sop, tx_eop} <= {tx_mod, tx_dval, tx_sop, tx_eop};
        end
        else {tx_mod, tx_dval, tx_sop, tx_eop} <= {2'b0, 1'b0, 1'b0, 1'b0};
   end
end
//============================================

发现阶段到此结束,本人做这个完全是为了学习,没有其他用途,工具本身是无害的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值