1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2002-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation.  You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //--------------------------------------------------------------------------
19 // cd_pppoe.cc author Josh Rosenbaum <jrosenba@cisco.com>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "codecs/codec_module.h"
26 #include "framework/codec.h"
27 
28 using namespace snort;
29 
30 namespace
31 {
32 enum class PppoepktType
33 {
34     DISCOVERY,
35     SESSION,
36 };
37 
38 /* PPPoEHdr Header; eth::EtherHdr plus the PPPoE Header */
39 struct PPPoEHdr
40 {
41     uint8_t ver_type;     /* pppoe version/type */
42     uint8_t code;         /* pppoe code CODE_* */
43     uint16_t session;     /* session id */
44     uint16_t length;      /* payload length */
45     /* payload follows */
46 };
47 
48 //-------------------------------------------------------------------------
49 // General PPPoEpkt module.
50 //
51 //      ***** NOTE: THE CODEC HAS A DIFFERENT NAME!
52 //          * Additionally, this module is used for generating a rule stub ONLY!
53 //          * If you want to create a module for configuration, you must change the
54 //          * names of the correct PPPoEpkt codec
55 //-------------------------------------------------------------------------
56 #define CD_PPPOE_NAME "pppoe"
57 static const RuleMap pppoe_rules[] =
58 {
59     { DECODE_BAD_PPPOE, "bad PPPOE frame detected" },
60     { 0, nullptr }
61 };
62 
63 #define pppoe_help \
64     "support for point-to-point protocol over ethernet"
65 
66 class PPPoEModule : public BaseCodecModule
67 {
68 public:
PPPoEModule()69     PPPoEModule() : BaseCodecModule(CD_PPPOE_NAME, pppoe_help) { }
70 
get_rules() const71     const RuleMap* get_rules() const override
72     { return pppoe_rules; }
73 };
74 
75 class PPPoECodec : public Codec
76 {
77 public:
78     bool decode(const RawData&, CodecData&, DecodeData&) final;
79     bool encode(const uint8_t* const raw_in, const uint16_t raw_len,
80         EncState&, Buffer&, Flow*) final;
81 
82 protected:
PPPoECodec(const char * s,PppoepktType type)83     PPPoECodec(const char* s, PppoepktType type) :
84         Codec(s),
85         ppp_type(type)
86     { }
87 
88 private:
89     PppoepktType ppp_type;
90 };
91 } // namespace
92 
93 constexpr uint16_t PPPOE_HEADER_LEN = 6;
94 
decode(const RawData & raw,CodecData & codec,DecodeData &)95 bool PPPoECodec::decode(const RawData& raw,
96     CodecData& codec,
97     DecodeData&)
98 {
99     if (raw.len < PPPOE_HEADER_LEN)
100     {
101         codec_event(codec, DECODE_BAD_PPPOE);
102         return false;
103     }
104 
105     if (ppp_type == PppoepktType::DISCOVERY)
106     {
107         return true;
108     }
109 
110     codec.lyr_len = PPPOE_HEADER_LEN;
111     codec.next_prot_id = ProtocolId::ETHERTYPE_PPP;
112     return true;
113 }
114 
115 /******************************************************************
116  ******************** E N C O D E R  ******************************
117  ******************************************************************/
118 
encode(const uint8_t * const raw_in,const uint16_t raw_len,EncState &,Buffer & buf,Flow *)119 bool PPPoECodec::encode(const uint8_t* const raw_in, const uint16_t raw_len,
120     EncState&, Buffer& buf, Flow*)
121 {
122     if (!buf.allocate(raw_len))
123         return false;
124 
125     memcpy(buf.data(), raw_in, raw_len);
126     PPPoEHdr* const ppph = reinterpret_cast<PPPoEHdr*>(buf.data());
127     ppph->length = htons((uint16_t)buf.size());
128 
129     return true;
130 }
131 
132 /*******************************************************************
133  *******************************************************************
134  *************                  CODECS              ****************
135  *******************************************************************
136  *******************************************************************/
137 
138 namespace
139 {
140 #define CD_PPPOEPKT_DISC_NAME "pppoe_disc"
141 #define CD_PPPOEPKT_DISC_HELP "support for point-to-point discovery"
142 
143 #define CD_PPPOEPKT_SESS_NAME "pppoe_sess"
144 #define CD_PPPOEPKT_SESS_HELP "support for point-to-point session"
145 
146 /*  'decode' and 'encode' functions are in the PPPoECodec */
147 class PPPoEDiscCodec : public PPPoECodec
148 {
149 public:
PPPoEDiscCodec()150     PPPoEDiscCodec() : PPPoECodec(CD_PPPOEPKT_DISC_NAME, PppoepktType::DISCOVERY) { }
151 
get_protocol_ids(std::vector<ProtocolId> & v)152     void get_protocol_ids(std::vector<ProtocolId>& v) override
153     { v.emplace_back(ProtocolId::ETHERTYPE_PPPOE_DISC); }
154 };
155 
156 class PPPoESessCodec : public PPPoECodec
157 {
158 public:
PPPoESessCodec()159     PPPoESessCodec() : PPPoECodec(CD_PPPOEPKT_SESS_NAME, PppoepktType::SESSION) { }
160 
get_protocol_ids(std::vector<ProtocolId> & v)161     void get_protocol_ids(std::vector<ProtocolId>& v) override
162     { v.emplace_back(ProtocolId::ETHERTYPE_PPPOE_SESS); }
163 };
164 } // namespace
165 
166 //-------------------------------------------------------------------------
167 // api
168 //-------------------------------------------------------------------------
169 
170 // ***  NOTE: THE CODEC AND MODULE HAVE A DIFFERENT NAME!
171 // since the module is only creating a rule stub and is NOT
172 // used for configuration, it doesn't matter. However, if you want to use the module
173 // for configuration, ensure the names are identical before continuing!
mod_ctor()174 static Module* mod_ctor()
175 { return new PPPoEModule; }
176 
mod_dtor(Module * m)177 static void mod_dtor(Module* m)
178 { delete m; }
179 
disc_ctor(Module *)180 static Codec* disc_ctor(Module*)
181 { return new PPPoEDiscCodec(); }
182 
sess_ctor(Module *)183 static Codec* sess_ctor(Module*)
184 { return new PPPoESessCodec(); }
185 
pppoe_dtor(Codec * cd)186 static void pppoe_dtor(Codec* cd)
187 { delete cd; }
188 
189 static const CodecApi pppoepkt_disc_api =
190 {
191     {
192         PT_CODEC,
193         sizeof(CodecApi),
194         CDAPI_VERSION,
195         0,
196         API_RESERVED,
197         API_OPTIONS,
198         CD_PPPOEPKT_DISC_NAME,
199         CD_PPPOEPKT_DISC_HELP,
200         mod_ctor,
201         mod_dtor,
202     },
203     nullptr,
204     nullptr,
205     nullptr,
206     nullptr,
207     disc_ctor,
208     pppoe_dtor,
209 };
210 
211 static const CodecApi pppoepkt_sess_api =
212 {
213     {
214         PT_CODEC,
215         sizeof(CodecApi),
216         CDAPI_VERSION,
217         0,
218         API_RESERVED,
219         API_OPTIONS,
220         CD_PPPOEPKT_SESS_NAME,
221         CD_PPPOEPKT_SESS_HELP,
222         nullptr,
223         nullptr,
224     },
225     nullptr,
226     nullptr,
227     nullptr,
228     nullptr,
229     sess_ctor,
230     pppoe_dtor,
231 };
232 
233 #ifdef BUILDING_SO
234 SO_PUBLIC const BaseApi* snort_plugins[] =
235 #else
236 const BaseApi* cd_pppoepkt[] =
237 #endif
238 {
239     &pppoepkt_disc_api.base,
240     &pppoepkt_sess_api.base,
241     nullptr
242 };
243 
244