Discovery phase interaction process
PPPoE (point to point protocol over Ethernet) is a point-to-point protocol based on Ethernet, including discovery phase and session phase. The discovery phase, PPPoE Discovery, aims to obtain the client MAC address and establish a connection. The discovery stage includes PADI, PADO, PADR and PADS. After the discovery phase, enter the standard PPP session phase. The interaction process in the discovery phase is shown in the following figure:
The data frame format is shown in the following figure:
The frame type domain discovery stage is 0x8863, and the session stage is 0x8864; The version and type are 0x01 in both the discovery phase and the session phase, and different types correspond to different phases of the code field; The default session ID is 0x0000. A unique session ID will be specified when the server sends PADS to the client; Length refers to the length of static load in bytes B.
PADI
PADI (PPPoE active discovery initiative) is a broadcast frame used to find an available server and sent by the client. The PADI group must contain at least one service type tag (Service Name Tag, field value 0x0101) to propose the required services to the server. When the client does not receive PADO within a certain period of time, the client will resend PADI and double the waiting time.
Destination address: 0xFFFF_FFFF
Frame type field: 0x8863
Code field: 0x09
Session ID: 0x0000
The PADI received by Wireshark is shown in the following figure:
Bytes without circles are static loads. When the PADI frame is received, the client MAC address, frame type, code ID and load frame length can be extracted according to the data frame format
//============================================ // Extract the client MAC address, frame type, code ID and load frame length //============================================ 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 //============================================
Loads should be stored in FIFO, because PADO loads are generated by PADI's host uniq tag and AC_ Name (server name)
//============================================ // fifo reading and writing //============================================ 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@(*)//The header is sent and the fifo count < = load frame length, write begin //The conditions for writing data must be strict, because the network will regularly send useless frames to maintain the link. The load of useless frames cannot be stored in fifo, otherwise the load of useless frames will be read out and added to PADO or PADS when reading next time 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): when the server receives PADI frame, the server sends PADO frame response request. The PADO frame must also contain at least one server name type label (the field value is 0x0102), indicating the type of service that can be provided to the host. PADO loads are generated by PADI's host uniq tag and AC_ Name (server name).
Destination address: extracted client MAC address
Frame type field: 0x8863
Code field: 0x07
Session ID: 0x0000
Wireshark receives the PADO sent by the server, as shown in the following figure:
The 4-byte data in the last circle is neither PADI's host uniq tag nor AC_name is the server name type label of PADO, and 0x000d represents the number of name bytes. When the frame length of PADI or PADR is extracted, the frame header of PADO and PADS can be obtained
//============================================ // Calculate the frame header in advance //============================================ 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 frame length + 16 'h11 is due to AC_name and server name type labels.
PADR
PADR (PPPoE active discovery request), the client selects one or more pados received, and then sends PADR to the selected server. PADR must also contain a service name type label.
Destination address: server MAC address
Frame type field: 0x8863
Code field: 0x19
Session ID: 0x0000
Wireshark receives the PADO sent by the server, as shown in the following figure:
The receiving process of PADR is the same as that of PADI, which extracts the client MAC address, frame type, code ID and load frame length, and stores the load in FIFO.
PADS
PADS (PPPoE active discovery session confirmation). After receiving PADR, the server sets up PADS frames. The host uniq tag values of PADS and PADR are the same.
Destination address: client MAC address
Frame type field: 0x8863
Code field: 0x65
Session ID: the value that uniquely identifies the session ID, which cannot be 0x0000
Wireshark receives the PADO sent by the server, as shown in the following figure:
The data behind the host uniq tag does not need to be sent, but is automatically added during transmission. To send PADO and PADS, a counter is required to count to determine when to send frame header, load and AC_name
//============================================ // PADO, PADS, FIFO count //============================================ 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 is empty, output AC_name else if ((n_state == PADO) && (cnt_PADO == 5'd6)) cnt_PADO <= 5'd6;//Read fifo else if((n_state == PADO) && (tx_eop == 1'b1)) cnt_PADO <= 5'b0;//After sending, reset else if (n_state == PADO) cnt_PADO <= cnt_PADO + 1'b1;//Transmit frame header 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;//Read fifo else if((n_state == PADS) && (tx_eop == 1'b1)) cnt_PADS <= 5'b0;//Reset after sending else if (n_state == PADS) cnt_PADS <= cnt_PADS + 1'b1;//Transmit frame header 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;//After sending the frame, send fifo else cnt_fifo <= 4'b0; end //============================================
Determine which data to send according to the count value
//============================================ // PADO, PADS send //============================================ 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;//Transmit load 5'd7: tx_data <= ac_name[159:128];//Send 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;//Transmit load 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 //============================================
This is the end of the discovery stage. I do this entirely for learning. There is no other use. The tool itself is harmless!