]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
nfs3: support NFS over UDP
authorVictor Julien <victor@inliniac.net>
Sat, 10 Jun 2017 21:19:29 +0000 (23:19 +0200)
committerVictor Julien <victor@inliniac.net>
Fri, 16 Jun 2017 11:11:36 +0000 (13:11 +0200)
rust/src/nfs/nfs3.rs
rust/src/nfs/parser.rs
src/Makefile.am
src/app-layer-nfs3-udp.c [new file with mode: 0644]
src/app-layer-nfs3-udp.h [new file with mode: 0644]
src/app-layer-parser.c
src/output-json-nfs3.c

index 0eb0249db4f15306a34288cbb4336208c1f3ec29..0adbc73703e2d5ce8b868420c25fb8eb2056bc09 100644 (file)
@@ -289,6 +289,8 @@ pub struct NFS3State {
     ts_gap: bool, // last TS update was gap
     tc_gap: bool, // last TC update was gap
 
+    is_udp: bool,
+
     /// tx counter for assigning incrementing id's to tx's
     tx_id: u64,
 
@@ -318,6 +320,7 @@ impl NFS3State {
             tc_ssn_gap:false,
             ts_gap:false,
             tc_gap:false,
+            is_udp:false,
             tx_id:0,
             de_state_count:0,
             //ts_txs_updated:false,
@@ -686,9 +689,11 @@ impl NFS3State {
                 tx.request_done = true;
             }
         }
-        self.ts_chunk_xid = r.hdr.xid;
-        let file_data_len = w.file_data.len() as u32 - fill_bytes as u32;
-        self.ts_chunk_left = w.file_len as u32 - file_data_len as u32;
+        if !self.is_udp {
+            self.ts_chunk_xid = r.hdr.xid;
+            let file_data_len = w.file_data.len() as u32 - fill_bytes as u32;
+            self.ts_chunk_left = w.file_len as u32 - file_data_len as u32;
+        }
         0
     }
 
@@ -994,8 +999,10 @@ impl NFS3State {
         //if is_last {
         //    self.tc_txs_updated = true;
         //}
-        self.tc_chunk_xid = r.hdr.xid;
-        self.tc_chunk_left = reply.count as u32 - reply.data.len() as u32;
+        if !self.is_udp {
+            self.tc_chunk_xid = r.hdr.xid;
+            self.tc_chunk_left = reply.count as u32 - reply.data.len() as u32;
+        }
 
         SCLogDebug!("REPLY {} to procedure {} blob size {} / {}: chunk_left {}",
                 r.hdr.xid, NFSPROC3_READ, r.prog_data.len(), reply.count, self.tc_chunk_left);
@@ -1321,6 +1328,43 @@ impl NFS3State {
         };
         status
     }
+    /// Parsing function
+    pub fn parse_udp_ts<'b>(&mut self, input: &'b[u8]) -> u32 {
+        let mut status = 0;
+        SCLogDebug!("parse_udp_ts ({})", input.len());
+        if input.len() > 0 {
+            match parse_rpc_udp_request(input) {
+                IResult::Done(_, ref rpc_record) => {
+                    self.is_udp = true;
+                    status |= self.process_request_record(rpc_record);
+                },
+                IResult::Incomplete(_) => {
+                },
+                IResult::Error(e) => { panic!("Parsing failed: {:?}",e); //break
+                },
+            }
+        }
+        status
+    }
+
+    /// Parsing function
+    pub fn parse_udp_tc<'b>(&mut self, input: &'b[u8]) -> u32 {
+        let mut status = 0;
+        SCLogDebug!("parse_udp_tc ({})", input.len());
+        if input.len() > 0 {
+            match parse_rpc_udp_reply(input) {
+                IResult::Done(_, ref rpc_record) => {
+                    self.is_udp = true;
+                    status |= self.process_reply_record(rpc_record);
+                },
+                IResult::Incomplete(_) => {
+                },
+                IResult::Error(e) => { panic!("Parsing failed: {:?}",e); //break
+                },
+            }
+        };
+        status
+    }
     fn getfiles(&mut self, direction: u8) -> * mut FileContainer {
         //SCLogDebug!("direction: {}", direction);
         if direction == STREAM_TOCLIENT {
@@ -1411,6 +1455,45 @@ pub extern "C" fn rs_nfs3_parse_response(_flow: *mut Flow,
     }
 }
 
+/// C binding parse a DNS request. Returns 1 on success, -1 on failure.
+#[no_mangle]
+pub extern "C" fn rs_nfs3_parse_request_udp(_flow: *mut Flow,
+                                       state: &mut NFS3State,
+                                       _pstate: *mut libc::c_void,
+                                       input: *mut libc::uint8_t,
+                                       input_len: libc::uint32_t,
+                                       _data: *mut libc::c_void)
+                                       -> libc::int8_t
+{
+    let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+    SCLogDebug!("parsing {} bytes of request data", input_len);
+
+    if state.parse_udp_ts(buf) == 0 {
+        1
+    } else {
+        -1
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_nfs3_parse_response_udp(_flow: *mut Flow,
+                                        state: &mut NFS3State,
+                                        _pstate: *mut libc::c_void,
+                                        input: *mut libc::uint8_t,
+                                        input_len: libc::uint32_t,
+                                        _data: *mut libc::c_void)
+                                        -> libc::int8_t
+{
+    SCLogDebug!("parsing {} bytes of response data", input_len);
+    let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+
+    if state.parse_udp_tc(buf) == 0 {
+        1
+    } else {
+        -1
+    }
+}
+
 #[no_mangle]
 pub extern "C" fn rs_nfs3_state_get_tx_count(state: &mut NFS3State)
                                             -> libc::uint64_t
@@ -1643,6 +1726,43 @@ pub fn nfs3_probe(i: &[u8], direction: u8) -> i8 {
     }
 }
 
+pub fn nfs3_probe_udp(i: &[u8], direction: u8) -> i8 {
+    if direction == STREAM_TOCLIENT {
+        match parse_rpc_udp_reply(i) {
+            IResult::Done(_, ref rpc) => {
+                if i.len() >= 32 && rpc.hdr.msgtype == 1 && rpc.reply_state == 0 && rpc.accept_state == 0 {
+                    SCLogNotice!("TC PROBE LEN {} XID {} TYPE {}", rpc.hdr.frag_len, rpc.hdr.xid, rpc.hdr.msgtype);
+                    return 1;
+                } else {
+                    return -1;
+                }
+            },
+            IResult::Incomplete(_) => {
+                return -1;
+            },
+            IResult::Error(_) => {
+                return -1;
+            },
+        }
+    } else {
+        match parse_rpc_udp_request(i) {
+            IResult::Done(_, ref rpc) => {
+                if i.len() >= 48 && rpc.hdr.msgtype == 0 && rpc.progver == 3 && rpc.program == 100003 {
+                    return 1;
+                } else {
+                    return -1;
+                }
+            },
+            IResult::Incomplete(_) => {
+                return -1;
+            },
+            IResult::Error(_) => {
+                return -1;
+            },
+        }
+    }
+}
+
 /// TOSERVER probe function
 #[no_mangle]
 pub extern "C" fn rs_nfs_probe_ts(input: *const libc::uint8_t, len: libc::uint32_t)
@@ -1664,6 +1784,26 @@ pub extern "C" fn rs_nfs_probe_tc(input: *const libc::uint8_t, len: libc::uint32
     return nfs3_probe(slice, STREAM_TOCLIENT);
 }
 
+/// TOSERVER probe function
+#[no_mangle]
+pub extern "C" fn rs_nfs_probe_udp_ts(input: *const libc::uint8_t, len: libc::uint32_t)
+                               -> libc::int8_t
+{
+    let slice: &[u8] = unsafe {
+        std::slice::from_raw_parts(input as *mut u8, len as usize)
+    };
+    return nfs3_probe_udp(slice, STREAM_TOSERVER);
+}
+/// TOCLIENT probe function
+#[no_mangle]
+pub extern "C" fn rs_nfs_probe_udp_tc(input: *const libc::uint8_t, len: libc::uint32_t)
+                               -> libc::int8_t
+{
+    let slice: &[u8] = unsafe {
+        std::slice::from_raw_parts(input as *mut u8, len as usize)
+    };
+    return nfs3_probe_udp(slice, STREAM_TOCLIENT);
+}
 
 #[no_mangle]
 pub extern "C" fn rs_nfs3_getfiles(direction: u8, ptr: *mut NFS3State) -> * mut FileContainer {
index 9eb91685f6b2f9488ae931907d3ef65d23102ea9..5a8714c32991e9860767f3353de5f6397cbef114 100644 (file)
@@ -635,3 +635,111 @@ named!(pub parse_rpc_reply<RpcReplyPacket>,
            }
    ))
 );
+
+named!(pub parse_rpc_udp_packet_header<RpcPacketHeader>,
+    do_parse!(
+        xid: be_u32
+        >> msgtype: be_u32
+        >> (
+            RpcPacketHeader {
+                frag_is_last:false,
+                frag_len:0,
+
+                xid:xid,
+                msgtype:msgtype,
+            }
+        ))
+);
+
+#[derive(Debug,PartialEq)]
+pub struct RpcUdpRequestPacket<'a> {
+    pub hdr: RpcPacketHeader<>,
+
+    pub rpcver: u32,
+    pub program: u32,
+    pub progver: u32,
+    pub procedure: u32,
+
+    pub creds_flavor: u32,
+    pub creds_len: u32,
+    pub creds: Option<&'a[u8]>,
+    pub creds_unix:Option<RpcRequestCredsUnix<'a>>,
+
+    pub verifier_flavor: u32,
+    pub verifier_len: u32,
+    pub verifier: Option<&'a[u8]>,
+
+    pub prog_data: &'a[u8],
+}
+
+named!(pub parse_rpc_udp_request<RpcPacket>,
+   do_parse!(
+       hdr: parse_rpc_udp_packet_header
+
+       >> rpcver: be_u32
+       >> program: be_u32
+       >> progver: be_u32
+       >> procedure: be_u32
+
+       >> creds_flavor: be_u32
+       >> creds_len: be_u32
+       >> creds: cond!(creds_flavor != 1 && creds_len > 0, take!(creds_len as usize))
+       >> creds_unix: cond!(creds_len > 0 && creds_flavor == 1, flat_map!(take!((creds_len) as usize),parse_rfc_request_creds_unix))
+
+       >> verifier_flavor: be_u32
+       >> verifier_len: be_u32
+       >> verifier: cond!(verifier_len > 0, take!(verifier_len as usize))
+
+       >> pl: rest
+
+       >> (
+           RpcPacket {
+                hdr:hdr,
+
+                rpcver:rpcver,
+                program:program,
+                progver:progver,
+                procedure:procedure,
+
+                creds_flavor:creds_flavor,
+                creds_len:creds_len,
+                creds:creds,
+                creds_unix:creds_unix,
+
+                verifier_flavor:verifier_flavor,
+                verifier_len:verifier_len,
+                verifier:verifier,
+
+                prog_data:pl,
+           }
+   ))
+);
+
+named!(pub parse_rpc_udp_reply<RpcReplyPacket>,
+   do_parse!(
+       hdr: parse_rpc_udp_packet_header
+
+       >> verifier_flavor: be_u32
+       >> verifier_len: be_u32
+       >> verifier: cond!(verifier_len > 0, take!(verifier_len as usize))
+
+       >> reply_state: be_u32
+       >> accept_state: be_u32
+
+       >> pl: rest
+
+       >> (
+           RpcReplyPacket {
+                hdr:hdr,
+
+                verifier_flavor:verifier_flavor,
+                verifier_len:verifier_len,
+                verifier:verifier,
+
+                reply_state:reply_state,
+                accept_state:accept_state,
+
+                prog_data:pl,
+           }
+   ))
+);
index c2a95c08659043754e913993e8f1101dde29bd6f..84f517979b29b93e8f0965587cdead69573acd61 100644 (file)
@@ -40,6 +40,7 @@ app-layer-smb2.c app-layer-smb2.h \
 app-layer-smb.c app-layer-smb.h \
 app-layer-smtp.c app-layer-smtp.h \
 app-layer-nfs3.c app-layer-nfs3.h \
+app-layer-nfs3-udp.c app-layer-nfs3-udp.h \
 app-layer-template.c app-layer-template.h \
 app-layer-ssh.c app-layer-ssh.h \
 app-layer-ssl.c app-layer-ssl.h \
diff --git a/src/app-layer-nfs3-udp.c b/src/app-layer-nfs3-udp.c
new file mode 100644 (file)
index 0000000..bbd3ebe
--- /dev/null
@@ -0,0 +1,395 @@
+/* Copyright (C) 2015 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * NFS3 application layer detector and parser for learning and
+ * nfs3 pruposes.
+ *
+ * This nfs3 implements a simple application layer for something
+ * like the NFS3 protocol running on port 2049.
+ */
+
+#include "suricata-common.h"
+#include "stream.h"
+#include "conf.h"
+
+#include "util-unittest.h"
+
+#include "app-layer-detect-proto.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-nfs3-udp.h"
+
+#ifndef HAVE_RUST
+void RegisterNFS3UDPParsers(void)
+{
+}
+
+#else
+
+#include "rust.h"
+#include "rust-nfs-nfs3-gen.h"
+
+/* The default port to probe for echo traffic if not provided in the
+ * configuration file. */
+#define NFS3_DEFAULT_PORT "2049"
+
+/* The minimum size for a RFC message. For some protocols this might
+ * be the size of a header. TODO actual min size is likely larger */
+#define NFS3_MIN_FRAME_LEN 32
+
+/* Enum of app-layer events for an echo protocol. Normally you might
+ * have events for errors in parsing data, like unexpected data being
+ * received. For echo we'll make something up, and log an app-layer
+ * level alert if an empty message is received.
+ *
+ * Example rule:
+ *
+ * alert nfs3 any any -> any any (msg:"SURICATA NFS3 empty message"; \
+ *    app-layer-event:nfs3.empty_message; sid:X; rev:Y;)
+ */
+enum {
+    NFS3_DECODER_EVENT_EMPTY_MESSAGE,
+};
+
+SCEnumCharMap nfs3_udp_decoder_event_table[] = {
+    {"EMPTY_MESSAGE", NFS3_DECODER_EVENT_EMPTY_MESSAGE},
+    { NULL, 0 }
+};
+
+static void *NFS3StateAlloc(void)
+{
+    return rs_nfs3_state_new();
+}
+
+static void NFS3StateFree(void *state)
+{
+    rs_nfs3_state_free(state);
+}
+
+/**
+ * \brief Callback from the application layer to have a transaction freed.
+ *
+ * \param state a void pointer to the NFS3State object.
+ * \param tx_id the transaction ID to free.
+ */
+static void NFS3StateTxFree(void *state, uint64_t tx_id)
+{
+    rs_nfs3_state_tx_free(state, tx_id);
+}
+
+#if 0
+static int NFS3StateGetEventInfo(const char *event_name, int *event_id,
+    AppLayerEventType *event_type)
+{
+    *event_id = SCMapEnumNameToValue(event_name, nfs3_decoder_event_table);
+    if (*event_id == -1) {
+        SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+                   "nfs3 enum map table.",  event_name);
+        /* This should be treated as fatal. */
+        return -1;
+    }
+
+    *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+    return 0;
+}
+
+static AppLayerDecoderEvents *NFS3GetEvents(void *state, uint64_t tx_id)
+{
+    NFS3State *nfs3_state = state;
+    NFS3Transaction *tx;
+
+    TAILQ_FOREACH(tx, &nfs3_state->tx_list, next) {
+        if (tx->tx_id == tx_id) {
+            return tx->decoder_events;
+        }
+    }
+
+    return NULL;
+}
+
+static int NFS3HasEvents(void *state)
+{
+    NFS3State *echo = state;
+    return echo->events;
+}
+#endif
+
+/**
+ * \brief Probe the input to see if it looks like echo.
+ *
+ * \retval ALPROTO_NFS3 if it looks like echo, otherwise
+ *     ALPROTO_UNKNOWN.
+ */
+static AppProto NFS3ProbingParserTS(uint8_t *input, uint32_t input_len,
+    uint32_t *offset)
+{
+    SCLogNotice("probing");
+    if (input_len < NFS3_MIN_FRAME_LEN) {
+        SCLogNotice("unknown");
+        return ALPROTO_UNKNOWN;
+    }
+
+    int8_t r = rs_nfs_probe_udp_ts(input, input_len);
+    if (r == 1) {
+        SCLogNotice("nfs3");
+        return ALPROTO_NFS3;
+    } else if (r == -1) {
+        SCLogNotice("failed");
+        return ALPROTO_FAILED;
+    }
+
+    SCLogNotice("Protocol not detected as ALPROTO_NFS3.");
+    return ALPROTO_UNKNOWN;
+}
+
+static AppProto NFS3ProbingParserTC(uint8_t *input, uint32_t input_len,
+    uint32_t *offset)
+{
+    SCLogNotice("probing");
+    if (input_len < NFS3_MIN_FRAME_LEN) {
+        SCLogNotice("unknown");
+        return ALPROTO_UNKNOWN;
+    }
+
+    int8_t r = rs_nfs_probe_tc(input, input_len);
+    if (r == 1) {
+        SCLogNotice("nfs3");
+        return ALPROTO_NFS3;
+    } else if (r == -1) {
+        SCLogNotice("failed");
+        return ALPROTO_FAILED;
+    }
+
+    SCLogNotice("Protocol not detected as ALPROTO_NFS3.");
+    return ALPROTO_UNKNOWN;
+}
+
+static int NFS3ParseRequest(Flow *f, void *state,
+    AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
+    void *local_data)
+{
+    uint16_t file_flags = FileFlowToFlags(f, STREAM_TOSERVER);
+    rs_nfs3_setfileflags(0, state, file_flags);
+
+    return rs_nfs3_parse_request_udp(f, state, pstate, input, input_len, local_data);
+}
+
+static int NFS3ParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
+    uint8_t *input, uint32_t input_len, void *local_data)
+{
+    uint16_t file_flags = FileFlowToFlags(f, STREAM_TOCLIENT);
+    rs_nfs3_setfileflags(1, state, file_flags);
+
+    return rs_nfs3_parse_response_udp(f, state, pstate, input, input_len, local_data);
+}
+
+static uint64_t NFS3GetTxCnt(void *state)
+{
+    return rs_nfs3_state_get_tx_count(state);
+}
+
+static void *NFS3GetTx(void *state, uint64_t tx_id)
+{
+    return rs_nfs3_state_get_tx(state, tx_id);
+}
+
+static void NFS3SetTxLogged(void *state, void *vtx, uint32_t logger)
+{
+    rs_nfs3_tx_set_logged(state, vtx, logger);
+}
+
+static int NFS3GetTxLogged(void *state, void *vtx, uint32_t logger)
+{
+    return rs_nfs3_tx_get_logged(state, vtx, logger);
+}
+
+/**
+ * \brief Called by the application layer.
+ *
+ * In most cases 1 can be returned here.
+ */
+static int NFS3GetAlstateProgressCompletionStatus(uint8_t direction) {
+    return rs_nfs3_state_progress_completion_status(direction);
+}
+
+/**
+ * \brief Return the state of a transaction in a given direction.
+ *
+ * In the case of the echo protocol, the existence of a transaction
+ * means that the request is done. However, some protocols that may
+ * need multiple chunks of data to complete the request may need more
+ * than just the existence of a transaction for the request to be
+ * considered complete.
+ *
+ * For the response to be considered done, the response for a request
+ * needs to be seen.  The response_done flag is set on response for
+ * checking here.
+ */
+static int NFS3GetStateProgress(void *tx, uint8_t direction)
+{
+    return rs_nfs3_tx_get_alstate_progress(tx, direction);
+}
+
+/**
+ * \brief get stored tx detect state
+ */
+static DetectEngineState *NFS3GetTxDetectState(void *vtx)
+{
+    return rs_nfs3_state_get_tx_detect_state(vtx);
+}
+
+/**
+ * \brief set store tx detect state
+ */
+static int NFS3SetTxDetectState(void *state, void *vtx,
+    DetectEngineState *s)
+{
+    rs_nfs3_state_set_tx_detect_state(state, vtx, s);
+    return 0;
+}
+
+static FileContainer *NFS3GetFiles(void *state, uint8_t direction)
+{
+    return rs_nfs3_getfiles(direction, state);
+}
+
+static StreamingBufferConfig sbcfg = STREAMING_BUFFER_CONFIG_INITIALIZER;
+static SuricataFileContext sfc = { &sbcfg };
+
+void RegisterNFS3UDPParsers(void)
+{
+    const char *proto_name = "nfs3";
+
+    /* Check if NFS3 TCP detection is enabled. If it does not exist in
+     * the configuration file then it will be enabled by default. */
+    if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
+
+        rs_nfs3_init(&sfc);
+
+        SCLogNotice("NFS3 UDP protocol detection enabled.");
+
+        AppLayerProtoDetectRegisterProtocol(ALPROTO_NFS3, proto_name);
+
+        if (RunmodeIsUnittests()) {
+
+            SCLogNotice("Unittest mode, registering default configuration.");
+            AppLayerProtoDetectPPRegister(IPPROTO_UDP, NFS3_DEFAULT_PORT,
+                ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
+                NFS3ProbingParserTS, NFS3ProbingParserTC);
+
+        }
+        else {
+
+            if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
+                    proto_name, ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN,
+                    NFS3ProbingParserTS, NFS3ProbingParserTC)) {
+                SCLogNotice("No NFS3 app-layer configuration, enabling NFS3"
+                    " detection TCP detection on port %s.",
+                    NFS3_DEFAULT_PORT);
+                AppLayerProtoDetectPPRegister(IPPROTO_UDP,
+                    NFS3_DEFAULT_PORT, ALPROTO_NFS3, 0,
+                    NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
+                    NFS3ProbingParserTS, NFS3ProbingParserTC);
+            }
+
+        }
+
+    }
+
+    else {
+        SCLogNotice("Protocol detecter and parser disabled for NFS3.");
+        return;
+    }
+
+    if (AppLayerParserConfParserEnabled("udp", proto_name))
+    {
+        SCLogNotice("Registering NFS3 protocol parser.");
+
+        /* Register functions for state allocation and freeing. A
+         * state is allocated for every new NFS3 flow. */
+        AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_NFS3,
+            NFS3StateAlloc, NFS3StateFree);
+
+        /* Register request parser for parsing frame from server to client. */
+        AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_NFS3,
+            STREAM_TOSERVER, NFS3ParseRequest);
+
+        /* Register response parser for parsing frames from server to client. */
+        AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_NFS3,
+            STREAM_TOCLIENT, NFS3ParseResponse);
+
+        /* Register a function to be called by the application layer
+         * when a transaction is to be freed. */
+        AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_NFS3,
+            NFS3StateTxFree);
+
+        AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_NFS3,
+            NFS3GetTxLogged, NFS3SetTxLogged);
+
+        /* Register a function to return the current transaction count. */
+        AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_NFS3,
+            NFS3GetTxCnt);
+
+        /* Transaction handling. */
+        AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_NFS3,
+            NFS3GetAlstateProgressCompletionStatus);
+        AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP,
+            ALPROTO_NFS3, NFS3GetStateProgress);
+        AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_NFS3,
+            NFS3GetTx);
+
+        AppLayerParserRegisterGetFilesFunc(IPPROTO_UDP, ALPROTO_NFS3, NFS3GetFiles);
+
+        /* Application layer event handling. */
+//        AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_NFS3,
+//            NFS3HasEvents);
+
+        /* What is this being registered for? */
+        AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_NFS3,
+            NULL, NFS3GetTxDetectState, NFS3SetTxDetectState);
+
+//        AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_NFS3,
+//            NFS3StateGetEventInfo);
+//        AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_NFS3,
+//            NFS3GetEvents);
+    }
+    else {
+        SCLogNotice("NFS3 protocol parsing disabled.");
+    }
+
+#ifdef UNITTESTS
+    AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_NFS3,
+        NFS3UDPParserRegisterTests);
+#endif
+}
+
+#ifdef UNITTESTS
+#endif
+
+void NFS3UDPParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+#endif
+}
+
+#endif /* HAVE_RUST */
diff --git a/src/app-layer-nfs3-udp.h b/src/app-layer-nfs3-udp.h
new file mode 100644 (file)
index 0000000..5c3be9a
--- /dev/null
@@ -0,0 +1,34 @@
+/* Copyright (C) 2017 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __APP_LAYER_NFS3_UDP_H__
+#define __APP_LAYER_NFS3_UDP_H__
+
+void RegisterNFS3UDPParsers(void);
+void NFS3UDPParserRegisterTests(void);
+
+/** Opaque Rust types. */
+typedef struct NFS3tate_ NFS3State;
+typedef struct NFS3Transaction_ NFS3Transaction;
+
+#endif /* __APP_LAYER_NFS3_H__ */
index 066e54bc472bc094cdc1a74d89a74c0329838112..8d91b332ff09f2e74973bc225245626e5b76343f 100644 (file)
@@ -61,6 +61,7 @@
 #include "app-layer-enip.h"
 #include "app-layer-dnp3.h"
 #include "app-layer-nfs3.h"
+#include "app-layer-nfs3-udp.h"
 #include "app-layer-template.h"
 
 #include "conf.h"
@@ -1384,6 +1385,7 @@ void AppLayerParserRegisterProtocolParsers(void)
     RegisterENIPTCPParsers();
     RegisterDNP3Parsers();
     RegisterNFS3Parsers();
+    RegisterNFS3UDPParsers();
     RegisterTemplateParsers();
 
     /** IMAP */
index 20f1d4d1d28916db2659ce2d6d1a126654073f48..ee988141de924fcc781fd01455a6efda4116d0da 100644 (file)
@@ -130,6 +130,7 @@ static OutputCtx *OutputNFS3LogInitSub(ConfNode *conf,
     SCLogDebug("NFS3 log sub-module initialized.");
 
     AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_NFS3);
+    AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_NFS3);
 
     return output_ctx;
 }