Look past third party header for packet filtering.

This commit is contained in:
wb2osz 2023-06-14 01:37:08 +01:00
parent f9cf42b291
commit e84a622515
6 changed files with 436 additions and 166 deletions

View File

@ -30,6 +30,14 @@
> Add: "FX25TX 1" (or 16 or 32 or 64)
### Bugs Fixed: ###
- The t/m packet filter incorrectly included bulletins. It now allows only "messages" to specific stations. Use of t/m is discouraged. i/180 is the preferred filter for messages to users recently heard locally.
- Packet filtering now skips over any third party header before classifying packet types.
## Version 1.6 -- October 2020 ##
### New Build Procedure: ###

View File

@ -169,7 +169,9 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
//dw_printf ("DEBUG decode_aprs info=\"%s\"\n", pinfo);
memset (A, 0, sizeof (*A));
if (third_party_src == NULL) {
memset (A, 0, sizeof (*A));
}
A->g_quiet = quiet;
@ -234,6 +236,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
memcpy(payload_src, (char*)pinfo+1, sizeof(payload_src)-1);
char *q = strchr(payload_src, '>');
if (q != NULL) *q = '\0';
A->g_has_thirdparty_header = 1;
decode_aprs (A, pp_payload, quiet, payload_src); // 1 means used recursively
ax25_delete (pp_payload);
return;
@ -318,6 +321,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
{
aprs_ll_pos (A, pinfo, info_len);
}
A->g_packet_type = packet_type_position;
break;
@ -330,10 +334,12 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
if (strncmp((char*)pinfo, "$ULTW", 5) == 0)
{
aprs_ultimeter (A, (char*)pinfo, info_len); // TODO: produce obsolete error.
A->g_packet_type = packet_type_weather;
}
else
{
aprs_raw_nmea (A, pinfo, info_len);
A->g_packet_type = packet_type_position;
}
break;
@ -341,17 +347,20 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
case '`': /* Current Mic-E Data (not used in TM-D700) */
aprs_mic_e (A, pp, pinfo, info_len);
A->g_packet_type = packet_type_position;
break;
case ')': /* Item. */
aprs_item (A, pinfo, info_len);
A->g_packet_type = packet_type_item;
break;
case '/': /* Position with timestamp (no APRS messaging) */
case '@': /* Position with timestamp (with APRS messaging) */
aprs_ll_pos_time (A, pinfo, info_len);
A->g_packet_type = packet_type_position;
break;
@ -360,42 +369,76 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
/* Telemetry metadata. */
aprs_message (A, pinfo, info_len, quiet);
switch (A->g_message_subtype) {
case message_subtype_message:
case message_subtype_ack:
case message_subtype_rej:
A->g_packet_type = packet_type_message;
break;
case message_subtype_nws:
A->g_packet_type = packet_type_nws;
break;
case message_subtype_bulletin:
default:
break;
case message_subtype_telem_parm:
case message_subtype_telem_unit:
case message_subtype_telem_eqns:
case message_subtype_telem_bits:
A->g_packet_type = packet_type_telemetry;
break;
case message_subtype_directed_query:
A->g_packet_type = packet_type_query;
break;
}
break;
case ';': /* Object */
aprs_object (A, pinfo, info_len);
A->g_packet_type = packet_type_object;
break;
case '<': /* Station Capabilities */
aprs_station_capabilities (A, (char*)pinfo, info_len);
A->g_packet_type = packet_type_capabilities;
break;
case '>': /* Status Report */
aprs_status_report (A, (char*)pinfo, info_len);
A->g_packet_type = packet_type_status;
break;
case '?': /* General Query */
aprs_general_query (A, (char*)pinfo, info_len, quiet);
A->g_packet_type = packet_type_query;
break;
case 'T': /* Telemetry */
aprs_telemetry (A, (char*)pinfo, info_len, quiet);
A->g_packet_type = packet_type_telemetry;
break;
case '_': /* Positionless Weather Report */
aprs_positionless_weather_report (A, pinfo, info_len);
A->g_packet_type = packet_type_weather;
break;
case '{': /* user defined data */
aprs_user_defined (A, (char*)pinfo, info_len);
A->g_packet_type = packet_type_userdefined;
break;
case 't': /* Raw touch tone data - NOT PART OF STANDARD */
@ -404,6 +447,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
/* Might move into user defined data, above. */
aprs_raw_touch_tone (A, (char*)pinfo, info_len);
// no packet type for t/ filter
break;
case 'm': /* Morse Code data - NOT PART OF STANDARD */
@ -413,6 +457,7 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_sr
/* Might move into user defined data, above. */
aprs_morse_code (A, (char*)pinfo, info_len);
// no packet type for t/ filter
break;
//case '}': /* third party header */
@ -1091,7 +1136,9 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen)
*
* Function: aprs_mic_e
*
* Purpose: Decode MIC-E (also Kenwood D7 & D700) packet.
* Purpose: Decode MIC-E (e.g. Kenwood D7 & D700) packet.
* This format is an overzelous quest to make the packet as short as possible.
* It uses non-printable characters and hacks wrapped in kludges.
*
* Inputs: info - Pointer to Information field.
* ilen - Information field length.
@ -1100,31 +1147,120 @@ static void aprs_raw_nmea (decode_aprs_t *A, unsigned char *info, int ilen)
*
* Description:
*
* Destination Address Field -
* AX.25 Destination Address Field -
*
* The 7-byte Destination Address field contains
* The 6-byte Destination Address field contains
* the following encoded information:
*
* * The 6 latitude digits.
* * A 3-bit Mic-E message identifier, specifying one of 7 Standard Mic-E
* Message Codes or one of 7 Custom Message Codes or an Emergency
* Message Code.
* * The North/South and West/East Indicators.
* * The Longitude Offset Indicator.
* * The generic APRS digipeater path code.
*
* Byte 1: Lat digit 1, message bit A
* Byte 2: Lat digit 2, message bit B
* Byte 3: Lat digit 3, message bit C
* Byte 4: Lat digit 4, N/S lat indicator
* Byte 5: Lat digit 5, Longitude offset
* Byte 6: Lat digit 6, W/E Long indicator
* *
* "Although the destination address appears to be quite unconventional, it is
* still a valid AX.25 address, consisting only of printable 7-bit ASCII values."
*
* References: Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt
* AX.25 Information Field - Starts with ' or `
*
* This is up to date with the 24 Aug 16 version mentioning the TH-D74.
* Bytes 1,2,3: Longitude
* Bytes 4,5,6: Speed and Course
* Byte 6: Symbol code
* Byte 7: Symbol Table ID
*
* The rest of it is a complicated comment field which can hold various information
* and must be intrepreted in a particular order. At this point we look for any
* prefix and/or suffix to identify the equipment type.
*
* References: Mic-E TYPE CODES -- http://www.aprs.org/aprs12/mic-e-types.txt
* Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt
*
* Next, we have what Addedum 1.2 calls the "type byte." This prefix can be
* space Original MIC-E.
* > Kenwood HT.
* ] Kenwood Mobile.
* none.
*
* We also need to look at the last byte or two
* for a possible suffix to distinguish equipment types. Examples:
* >...... is D7
* >......= is D72
* >......^ is D74
*
* For other brands, it gets worse. There might a 2 character suffix.
* The prefix indicates whether messaging-capable. Examples:
* `....._.% Yaesu FTM-400DR
* `......_) Yaesu FTM-100D
* `......_3 Yaesu FT5D
*
* '......|3 Byonics TinyTrack3
* '......|4 Byonics TinyTrack4
*
* Any prefix and suffix must be removed before futher processsing.
*
* Pick one: MIC-E Telemetry Data or "Status Text" (called a comment everywhere else).
*
* If the character after the symbol table id is "," (comma) or 0x1d, we have telemetry.
* (Is this obsoleted by the base-91 telemetry?)
*
* ` Two 2-character hexadecimal numbers. (Channels 1 & 3)
* ' Five 2-character hexadecimal numbers.
*
* Anything left over is a comment which can contain various types of information.
*
* If present, the MIC-E compressed altitude must be first.
* It is three base-91 characters followed by "}".
* Examples: "4T} "4T} ]"4T}
*
* We can also have frequency specification -- http://www.aprs.org/info/freqspec.tx
*
* Warning: Some Kenwood radios add CR at the end, in apparent violation of the spec.
* Watch out so it doesn't get included when looking for equipment type suffix.
*
* Mic-E TEST EXAMPLES -- http://www.aprs.org/aprs12/mic-e-examples.txt
*
* Examples: `b9Z!4y>/>"4N}Paul's_TH-D7
* Examples: Observed on the air.
*
* TODO: Destination SSID can contain generic digipeater path.
* KB1HNZ-9>TSSP5P,W1IMD,WIDE1,KQ1L-8,N3LLO-3,WIDE2*:`b6,l}#>/]"48}449.225MHz<0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff><0xff>=<0x0d>
*
* ` b6, l}# >/ ] "48} 449.225MHz ...... = <0x0d>
* mic-e long. cs sym prefix alt. freq comment suffix must-ignore
* Kenwood D710
*---------------
*
* N1JDU-9>ECCU8Y,W1MHL*,WIDE2-1:'cZ<0x7f>l#H>/]Go fly a kite!<0x0d>
*
* ' cZ<0x7f> l#H >/ ] ..... <0x0d>
* mic-e long. cs sym prefix comment no-suffix must-ignore
* Kenwood D700
*---------------
*
* KC1HHO-7>T2PX5R,WA1PLE-4,WIDE1*,WIDE2-1:`c_snp(k/`"4B}official relay station NTS_(<0x0d>
*
* ` c_s np( k/ ` "4B} ....... _( <0x0d>
* mic-e long. cs sym prefix alt comment suffix must-ignore
* FT2D
*---------------
*
* N1CMD-12>T3PQ1Y,KA1GJU-3,WIDE1,WA1PLE-4*:`cP#l!Fk/'"7H}|!%&-']|!w`&!|3
*
* ` cP# l!F k/ ' "7H} |!%&-']| !w`&! |3
* mic-e long. cs sym prefix alt base91telemetry DAO suffix
* TinyTrack3
*---------------
*
* W1STJ-3>T2UR4X,WA1PLE-4,WIDE1*,WIDE2-1:`c@&l#.-/`"5,}146.685MHz T100 -060 146.520 Simplex or Voice Alert_%<0x0d>
*
* ` c@& l#. -/ ` "5,} 146.685MHz T100 -060 .............. _% <0x0d>
* mic-e long. cs sym prefix alt frequency-specification comment suffix must-ignore
* FTM-400DR
*---------------
*
*
*
*
* TODO: Destination SSID can contain generic digipeater path. (?)
*
* Bugs: Doesn't handle ambiguous position. "space" treated as zero.
* Invalid data results in a message but latitude is not set to unknown.
@ -1507,6 +1643,8 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
// This does not change very often but I'm wondering if we could parse
// http://www.aprs.org/aprs12/mic-e-types.txt similar to how we use tocalls.txt.
// TODO: Use https://github.com/aprsorg/aprs-deviceid rather than hardcoding.
if (isT(*pfirst)) {
// "legacy" formats.
@ -1515,6 +1653,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
else if (*pfirst == '>' && *plast == '=') { strlcpy (A->g_mfr, "Kenwood TH-D72", sizeof(A->g_mfr)); pfirst++; plast--; }
else if (*pfirst == '>' && *plast == '^') { strlcpy (A->g_mfr, "Kenwood TH-D74", sizeof(A->g_mfr)); pfirst++; plast--; }
else if (*pfirst == '>' && *plast == '&') { strlcpy (A->g_mfr, "Kenwood TH-D75", sizeof(A->g_mfr)); pfirst++; plast--; }
else if (*pfirst == '>' ) { strlcpy (A->g_mfr, "Kenwood TH-D7A", sizeof(A->g_mfr)); pfirst++; }
else if (*pfirst == ']' && *plast == '=') { strlcpy (A->g_mfr, "Kenwood TM-D710", sizeof(A->g_mfr)); pfirst++; plast--; }
@ -1540,6 +1679,7 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
// ' should be used for trackers (not message capable).
else if (*pfirst == '\'' && *(plast-1) == '(' && *plast == '5') { strlcpy (A->g_mfr, "Anytone D578UV", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '\'' && *(plast-1) == '(' && *plast == '8') { strlcpy (A->g_mfr, "Anytone D878UV", sizeof(A->g_mfr)); pfirst++; plast-=2; }
else if (*pfirst == '\'' && *(plast-1) == '|' && *plast == '3') { strlcpy (A->g_mfr, "Byonics TinyTrack3", sizeof(A->g_mfr)); pfirst++; plast-=2; }
@ -1635,6 +1775,10 @@ static void aprs_mic_e (decode_aprs_t *A, packet_t pp, unsigned char *info, int
*
* Cases: :BLNxxxxxx: ... Bulletin.
* :NWSxxxxxx: ... National Weather Service Bulletin.
* http://www.aprs.org/APRS-docs/WX.TXT
* :SKYxxxxxx: ... Need reference.
* :CWAxxxxxx: ... Need reference.
* :BOMxxxxxx: ... Australian version.
*
* :xxxxxxxxx:PARM. Telemetry metadata, parameter name
* :xxxxxxxxx:UNIT. Telemetry metadata, unit/label
@ -1768,19 +1912,36 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
strlcpy (A->g_comment, p->message, sizeof(A->g_comment));
}
#warning = double check.
// Weather bulletins have addressee starting with NWS, SKY, CWA, or BOM.
// The protocol spec and http://www.aprs.org/APRS-docs/WX.TXT state that
// the 3 letter prefix must be followed by a dash.
// However, https://www.aprs-is.net/WX/ also lists the underscore
// alternative for the compressed format. Xastir implements this.
else if (strlen(addressee) >= 3 && strncmp(addressee,"NWS",3) == 0) {
// NWS-xxxxx
if (strlen(addressee) >=4 && addressee[3] == '-') {
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "NWS bulletin with identifier \"%s\"", addressee+4);
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin with identifier \"%s\"", addressee+4);
}
else if (strlen(addressee) >=4 && addressee[3] == '_') {
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Compressed Weather bulletin with identifier \"%s\"", addressee+4);
}
else {
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "NWS bulletin with identifier \"%s\", missing - after NWS", addressee+3);
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin is missing - or _ after %.3s", addressee);
}
A->g_message_subtype = message_subtype_nws;
strlcpy (A->g_comment, p->message, sizeof(A->g_comment));
}
else if (strlen(addressee) >= 3 && (strncmp(addressee,"SKY",3) == 0 || strncmp(addressee,"CWA",3) == 0 || strncmp(addressee,"BOM",3) == 0)) {
// SKY... or CWA... https://www.aprs-is.net/WX/
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Weather bulletin with identifier \"%s\"", addressee+4);
A->g_message_subtype = message_subtype_nws;
strlcpy (A->g_comment, p->message, sizeof(A->g_comment));
}
/*
* Special message formats contain telemetry metadata.
@ -1794,22 +1955,22 @@ static void aprs_message (decode_aprs_t *A, unsigned char *info, int ilen, int q
*/
else if (strncmp(p->message,"PARM.",5) == 0) {
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Parameter Name Message for \"%s\"", addressee);
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Parameter Name for \"%s\"", addressee);
A->g_message_subtype = message_subtype_telem_parm;
telemetry_name_message (addressee, p->message+5);
}
else if (strncmp(p->message,"UNIT.",5) == 0) {
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Unit/Label Message for \"%s\"", addressee);
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Unit/Label for \"%s\"", addressee);
A->g_message_subtype = message_subtype_telem_unit;
telemetry_unit_label_message (addressee, p->message+5);
}
else if (strncmp(p->message,"EQNS.",5) == 0) {
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Equation Coefficients Message for \"%s\"", addressee);
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Equation Coefficients for \"%s\"", addressee);
A->g_message_subtype = message_subtype_telem_eqns;
telemetry_coefficents_message (addressee, p->message+5, quiet);
}
else if (strncmp(p->message,"BITS.",5) == 0) {
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Bit Sense/Project Name Message for \"%s\"", addressee);
snprintf (A->g_data_type_desc, sizeof(A->g_data_type_desc), "Telemetry Bit Sense/Project Name for \"%s\"", addressee);
A->g_message_subtype = message_subtype_telem_bits;
telemetry_bit_sense_message (addressee, p->message+5, quiet);
}

View File

@ -24,7 +24,8 @@ typedef struct decode_aprs_s {
int g_quiet; /* Suppress error messages when decoding. */
char g_src[AX25_MAX_ADDR_LEN];
char g_src[AX25_MAX_ADDR_LEN]; // In the case of a packet encapsulated by a 3rd party
// header, this is the encapsulated source.
char g_dest[AX25_MAX_ADDR_LEN];
@ -66,6 +67,24 @@ typedef struct decode_aprs_s {
/* Also for Directed Station Query which is a */
/* special case of message. */
// This is so pfilter.c:filt_t does not need to duplicate the same work.
int g_has_thirdparty_header;
enum packet_type_e {
packet_type_none=0,
packet_type_position,
packet_type_weather,
packet_type_object,
packet_type_item,
packet_type_message,
packet_type_query,
packet_type_capabilities,
packet_type_status,
packet_type_telemetry,
packet_type_userdefined,
packet_type_nws
} g_packet_type;
enum message_subtype_e { message_subtype_invalid = 0,
message_subtype_message,
message_subtype_ack,

View File

@ -1749,9 +1749,34 @@ static void * satgate_delay_thread (void *arg)
*
*--------------------------------------------------------------------*/
// TODO: Use of "message" here is confusing because that term already
// has a special meaning for APRS. This could be an APRS message or
// some other APRS data type. Payload is already used.
#warning - clean up
// It is unforunate that the : data type indicator (DTI) was overloaded with
// so many different meanings. Simply looking at the DTI is not adequate for
// determining whether a packet is a message.
// We need to exclude the other special cases of telemetry metadata,
// bulletins, and weather bulletins.
static int is_message_message (char *infop)
{
if (*infop != ':') return (0);
if (strlen(infop) < 11) return (0); // too short for : addressee :
if (strlen(infop) >= 16) {
if (strncmp(infop+10, ":PARM.", 6) == 0) return (0);
if (strncmp(infop+10, ":UNIT.", 6) == 0) return (0);
if (strncmp(infop+10, ":EQNS.", 6) == 0) return (0);
if (strncmp(infop+10, ":BITS.", 6) == 0) return (0);
}
if (strlen(infop) >= 4) {
if (strncmp(infop+1, "BLN", 3) == 0) return (0);
if (strncmp(infop+1, "NWS", 3) == 0) return (0);
if (strncmp(infop+1, "SKY", 3) == 0) return (0);
if (strncmp(infop+1, "CWA", 3) == 0) return (0);
if (strncmp(infop+1, "BOM", 3) == 0) return (0);
}
return (1); // message, including ack, rej
}
static void maybe_xmit_packet_from_igate (char *message, int to_chan)
{
@ -1843,7 +1868,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan)
* If we recently transmitted a 'message' from some station,
* send the position of the message sender when it comes along later.
*
* Some refer to this as a courtesy posit report but I don't
* Some refer to this as a "courtesy posit report" but I don't
* think that is an official term.
*
* If we have a position report, look up the sender and see if we should
@ -1988,10 +2013,7 @@ static void maybe_xmit_packet_from_igate (char *message, int to_chan)
#endif
stats_rf_xmit_packets++; // Any type of packet.
// TEMP TEST: metadata temporarily allowed during testing.
if (*pinfo == ':' && ! is_telem_metadata(pinfo)) {
// temp test // if (*pinfo == ':') {
if (is_message_message(pinfo)) {
// We transmitted a "message." Telemetry metadata is excluded.
// Remember to pass along address of the sender later.
@ -2449,6 +2471,26 @@ void ig_to_tx_remember (packet_t pp, int chan, int bydigi)
}
}
#warning remove
static int is_message_overload (char *infop)
{
if (*infop != ':') return (0);
if (strlen(infop) < 16) return (0);
if (strncmp(infop+10, ":PARM.", 6) == 0) return (1);
if (strncmp(infop+10, ":UNIT.", 6) == 0) return (1);
if (strncmp(infop+10, ":EQNS.", 6) == 0) return (1);
if (strncmp(infop+10, ":BITS.", 6) == 0) return (1);
if (strncmp(infop+1, "BLN", 3) == 0) return (1);
if (strncmp(infop+1, "NWS", 3) == 0) return (1);
if (strncmp(infop+1, "SKY", 3) == 0) return (1);
if (strncmp(infop+1, "CWA", 3) == 0) return (1);
if (strncmp(infop+1, "BOM", 3) == 0) return (1);
return (0);
}
static int ig_to_tx_allow (packet_t pp, int chan)
{
unsigned short crc = ax25_dedupe_crc(pp);
@ -2481,7 +2523,7 @@ static int ig_to_tx_allow (packet_t pp, int chan)
/* We have a duplicate within some time period. */
if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) {
if (is_message_message((char*)pinfo)) {
/* I think I want to avoid the duplicate suppression for "messages." */
/* Suppose we transmit a message from station X and it doesn't get an ack back. */
@ -2530,7 +2572,7 @@ static int ig_to_tx_allow (packet_t pp, int chan)
/* the normal limit for them. */
increase_limit = 1;
if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) {
if (is_message_message((char*)pinfo)) {
increase_limit = 3;
}

View File

@ -336,6 +336,52 @@ void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, r
*/
hops = ax25_get_heard(pp) - AX25_SOURCE;
/*
* Consider the following scenario:
*
* (1) We hear AA1PR-9 by a path of 4 digipeaters.
* Looking closer, it's probably only two because there are left over WIDE1-0 and WIDE2-0.
*
* Digipeater WIDE2 (probably N3LLO-3) audio level = 72(19/15) [NONE] _|||||___
* [0.3] AA1PR-9>APY300,K1EQX-7,WIDE1,N3LLO-3,WIDE2*,ARISS::ANSRVR :cq hotg vt aprsthursday{01<0x0d>
* ----- -----
*
* (2) APRS-IS sends a response to us.
*
* [ig>tx] ANSRVR>APWW11,KJ4ERJ-15*,TCPIP*,qAS,KJ4ERJ-15::AA1PR-9 :N:HOTG 161 Messages Sent{JL}
*
* (3) Here is our analysis of whether it should be sent to RF.
*
* Was message addressee AA1PR-9 heard in the past 180 minutes, with 2 or fewer digipeater hops?
* No, AA1PR-9 was last heard over the radio with 4 digipeater hops 0 minutes ago.
*
* The wrong hop count caused us to drop a packet that should have been transmitted.
* We could put in a hack to not count the "WIDEn-0" addresses.
* That is not correct because other prefixes could be used and we don't know
* what they are for other digipeaters.
* I think the best solution is to simply ignore the hop count.
* Maybe next release will have a major cleanup.
*/
// HACK - Reduce hop count by number of used WIDEn-0 addresses.
if (hops > 1) {
for (int k = 0; k < ax25_get_num_repeaters(pp); k++) {
char digi[AX25_MAX_ADDR_LEN];
ax25_get_addr_no_ssid (pp, AX25_REPEATER_1 + k, digi);
int ssid = ax25_get_ssid (pp, AX25_REPEATER_1 + k);
int used = ax25_get_h (pp, AX25_REPEATER_1 + k);
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Examining %s-%d used=%d.\n", digi, ssid, used);
if (used && strlen(digi) == 5 && strncmp(digi, "WIDE", 4) == 0 && isdigit(digi[4]) && ssid == 0) {
hops--;
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("Decrease hop count to %d for problematic %s.\n", hops, digi);
}
}
}
mptr = mheard_ptr(source);
if (mptr == NULL) {
@ -571,7 +617,7 @@ void mheard_save_is (char *ptext)
* 8 for RF_CNT.
*
* time_limit - Include only stations heard within this many minutes.
* Typically 30 or 60.
* Typically 180.
*
* Returns: Number to be used in the statistics report.
*
@ -649,7 +695,7 @@ int mheard_count (int max_hops, int time_limit)
* callsign - Callsign for station.
*
* time_limit - Include only stations heard within this many minutes.
* Typically 30 or 60.
* Typically 180.
*
* max_hops - Include only stations heard with this number of
* digipeater hops or less. For reporting, we might use:

View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2015, 2016 John Langner, WB2OSZ
// Copyright (C) 2015, 2016, 2023 John Langner, WB2OSZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -546,7 +546,8 @@ static int parse_filter_spec (pfstate_t *pf)
/* b - budlist */
else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) {
/* Budlist - source address */
/* Budlist - AX.25 source address */
/* Could be different than source encapsulated by 3rd party header. */
char addr[AX25_MAX_ADDR_LEN];
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr);
result = filt_bodgu (pf, addr);
@ -572,7 +573,7 @@ static int parse_filter_spec (pfstate_t *pf)
else if (pf->token_str[0] == 'd' && ispunct(pf->token_str[1])) {
int n;
// loop on all digipeaters
// Loop on all AX.25 digipeaters.
result = 0;
for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
// Consider only those with the H (has-been-used) bit set.
@ -599,7 +600,7 @@ static int parse_filter_spec (pfstate_t *pf)
else if (pf->token_str[0] == 'v' && ispunct(pf->token_str[1])) {
int n;
// loop on all digipeaters (mnemonic Via)
// loop on all AX.25 digipeaters (mnemonic Via)
result = 0;
for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
// This is different than the previous "d" filter.
@ -623,10 +624,15 @@ static int parse_filter_spec (pfstate_t *pf)
}
}
/* g - Addressee of message. */
/* g - Addressee of message. e.g. "BLN*" for bulletins. */
else if (pf->token_str[0] == 'g' && ispunct(pf->token_str[1])) {
if (ax25_get_dti(pf->pp) == ':') {
if (pf->decoded.g_message_subtype == message_subtype_message ||
pf->decoded.g_message_subtype == message_subtype_ack ||
pf->decoded.g_message_subtype == message_subtype_rej ||
pf->decoded.g_message_subtype == message_subtype_bulletin ||
pf->decoded.g_message_subtype == message_subtype_nws ||
pf->decoded.g_message_subtype == message_subtype_directed_query) {
result = filt_bodgu (pf, pf->decoded.g_addressee);
if (s_debug >= 2) {
@ -643,7 +649,7 @@ static int parse_filter_spec (pfstate_t *pf)
}
}
/* u - unproto (destination) */
/* u - unproto (AX.25 destination) */
else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) {
/* Probably want to exclude mic-e types */
@ -668,7 +674,7 @@ static int parse_filter_spec (pfstate_t *pf)
}
}
/* t - type: position, weather, etc. */
/* t - packet type: position, weather, telemetry, etc. */
else if (pf->token_str[0] == 't' && ispunct(pf->token_str[1])) {
@ -728,7 +734,7 @@ static int parse_filter_spec (pfstate_t *pf)
(void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
text_color_set(DW_COLOR_DEBUG);
if (*infop == ':' && ! is_telem_metadata(infop)) {
if (pf->decoded.g_packet_type == packet_type_message) {
dw_printf (" %s returns %s for message to %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee);
}
else {
@ -832,31 +838,17 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
* 0 = no
* -1 = error detected
*
* Description: The filter is based the type filtering described here:
* Description: The filter is loosely based the type filtering described here:
* http://www.aprs-is.net/javAPRSFilter.aspx
*
* Most of these simply check the first byte of the information part.
* Trying to detect NWS information is a little trickier.
* Mostly use g_packet_type and g_message_subtype from decode_aprs.
*
* References:
* http://www.aprs-is.net/WX/
* http://wxsvr.aprs.net.au/protocol-new.html
* http://wxsvr.aprs.net.au/protocol-new.html (has disappeared)
*
*------------------------------------------------------------------------------*/
/* Telemetry metadata is a special case of message. */
/* We want to categorize it as telemetry rather than message. */
int is_telem_metadata (char *infop)
{
if (*infop != ':') return (0);
if (strlen(infop) < 16) return (0);
if (strncmp(infop+10, ":PARM.", 6) == 0) return (1);
if (strncmp(infop+10, ":UNIT.", 6) == 0) return (1);
if (strncmp(infop+10, ":EQNS.", 6) == 0) return (1);
if (strncmp(infop+10, ":BITS.", 6) == 0) return (1);
return (0);
}
static int filt_t (pfstate_t *pf)
{
char src[AX25_MAX_ADDR_LEN];
@ -873,108 +865,60 @@ static int filt_t (pfstate_t *pf)
switch (*f) {
case 'p': /* Position */
if (*infop == '!') return (1);
if (*infop == '/') return (1);
if (*infop == '=') return (1);
if (*infop == '@') return (1);
if (*infop == '\'') return (1); // MIC-E
if (*infop == '`') return (1); // MIC-E
// What if we have "_" symbol code for weather?
// Still consider as position.
// The same packet can match more than one type here.
if (pf->decoded.g_packet_type == packet_type_position) return(1);
break;
case 'o': /* Object */
if (*infop == ';') return (1);
if (pf->decoded.g_packet_type == packet_type_object) return(1);
break;
case 'i': /* Item */
if (*infop == ')') return (1);
if (pf->decoded.g_packet_type == packet_type_item) return(1);
break;
case 'm': /* Message */
if (*infop == ':' && ! is_telem_metadata(infop)) return (1);
case 'm': // Any "message."
if (pf->decoded.g_packet_type == packet_type_message) return(1);
break;
case 'q': /* Query */
if (*infop == '?') return (1);
if (pf->decoded.g_packet_type == packet_type_query) return(1);
break;
case 'c': /* station Capabilities - my extension */
/* Most often used for IGate statistics. */
if (*infop == '<') return (1);
if (pf->decoded.g_packet_type == packet_type_capabilities) return(1);
break;
case 's': /* Status */
if (*infop == '>') return (1);
if (pf->decoded.g_packet_type == packet_type_status) return(1);
break;
case 't': /* Telemetry */
if (*infop == 'T') return (1);
if (is_telem_metadata(infop)) return (1);
case 't': /* Telemetry data or metadata */
if (pf->decoded.g_packet_type == packet_type_telemetry) return(1);
break;
case 'u': /* User-defined */
if (*infop == '{') return (1);
if (pf->decoded.g_packet_type == packet_type_userdefined) return(1);
break;
case 'h': /* third party Header - my extension */
if (*infop == '}') return (1);
case 'h': /* has third party Header - my extension */
if (pf->decoded.g_has_thirdparty_header) return (1);
break;
case 'w': /* Weather */
if (*infop == '*') return (1); // Peet Bros
if (*infop == '_') return (1); // Weather report, no position.
if (strncmp(infop, "!!", 2) == 0) return(1); // Ultimeter 2000.
/* '$' is normally raw GPS. Check for special case. */
if (strncmp(infop, "$ULTW", 5) == 0) return (1);
/* Positions !=/@ with symbol code _ are weather. */
if (strchr("!=/@", *infop) != NULL &&
pf->decoded.g_symbol_code == '_') return (1);
if (pf->decoded.g_packet_type == packet_type_weather) return(1);
/* Positions !=/@ with symbol code _ are weather. */
/* Object with _ symbol is also weather. APRS protocol spec page 66. */
// Can't use *infop because it would not work with 3rd party header.
if (*infop == ';' &&
pf->decoded.g_symbol_code == '_') return (1);
// TODO: need more test cases at end for new weather cases.
if ((pf->decoded.g_packet_type == packet_type_position ||
pf->decoded.g_packet_type == packet_type_object) && pf->decoded.g_symbol_code == '_') return (1);
break;
case 'n': /* NWS format */
/*
* This is the interesting case.
* The source must be exactly 6 upper case letters, no SSID.
*/
if (strlen(src) != 6) break;
if (! isupper(src[0])) break;
if (! isupper(src[1])) break;
if (! isupper(src[2])) break;
if (! isupper(src[3])) break;
if (! isupper(src[4])) break;
if (! isupper(src[5])) break;
/*
* We can have a "message" with addressee starting with NWS, SKY, or BOM (Australian version.)
*/
if (strncmp(infop, ":NWS", 4) == 0) return (1);
if (strncmp(infop, ":SKY", 4) == 0) return (1);
if (strncmp(infop, ":BOM", 4) == 0) return (1);
/*
* Or we can have an object.
* It's not exactly clear how to distinguish this from other objects.
* It looks like the first 3 characters of the source should be the same
* as the first 3 characters of the addressee.
*/
if (infop[0] == ';' &&
infop[1] == src[0] &&
infop[2] == src[1] &&
infop[3] == src[2]) return (1);
if (pf->decoded.g_packet_type == packet_type_nws) return(1);
break;
default:
@ -990,6 +934,7 @@ static int filt_t (pfstate_t *pf)
/*------------------------------------------------------------------------------
*
* Name: filt_r
@ -1327,8 +1272,36 @@ static int filt_s (pfstate_t *pf)
*
* IMHO, the rules here are too restrictive.
*
* (1) The APRS-IS would send a "message" to my IGate only if the addressee
* The APRS-IS would send a "message" to my IGate only if the addressee
* has been heard nearby recently. 180 minutes, I believe.
* Why would I not want to transmit it?
*
* Discussion: In retrospect, I think this is far too complicated.
* In a future release, I think at options other than time should be removed.
* Messages have more value than most packets. Why reduce the chance of successful delivery?
*
* Consider the following scenario:
*
* (1) We hear AA1PR-9 by a path of 4 digipeaters.
* Looking closer, it's probably only two because there are left over WIDE1-0 and WIDE2-0.
*
* Digipeater WIDE2 (probably N3LLO-3) audio level = 72(19/15) [NONE] _|||||___
* [0.3] AA1PR-9>APY300,K1EQX-7,WIDE1,N3LLO-3,WIDE2*,ARISS::ANSRVR :cq hotg vt aprsthursday{01<0x0d>
*
* (2) APRS-IS sends a response to us.
*
* [ig>tx] ANSRVR>APWW11,KJ4ERJ-15*,TCPIP*,qAS,KJ4ERJ-15::AA1PR-9 :N:HOTG 161 Messages Sent{JL}
*
* (3) Here is our analysis of whether it should be sent to RF.
*
* Was message addressee AA1PR-9 heard in the past 180 minutes, with 2 or fewer digipeater hops?
* No, AA1PR-9 was last heard over the radio with 4 digipeater hops 0 minutes ago.
*
* The wrong hop count caused us to drop a packet that should have been transmitted.
* We could put in a hack to not count the "WIDE*-0" addresses.
* That is not correct because other prefixes could be used and we don't know
* what they are for other digipeaters.
* I think the best solution is to simply ignore the hop count.
*
*------------------------------------------------------------------------------*/
@ -1357,9 +1330,9 @@ static int filt_i (pfstate_t *pf)
double km = G_UNKNOWN;
char src[AX25_MAX_ADDR_LEN];
char *infop = NULL;
int info_len;
//char src[AX25_MAX_ADDR_LEN];
//char *infop = NULL;
//int info_len;
//char *f;
//char addressee[AX25_MAX_ADDR_LEN];
@ -1433,20 +1406,7 @@ static int filt_i (pfstate_t *pf)
* Get source address and info part.
* Addressee has already been extracted into pf->decoded.g_addressee.
*/
memset (src, 0, sizeof(src));
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, src);
info_len = ax25_get_info (pf->pp, (unsigned char **)(&infop));
if (infop == NULL) return (0);
if (info_len < 1) return (0);
// Determine packet type. We are interested only in "message."
// Telemetry metadata is not considered a message.
if (*infop != ':') return (0);
if (is_telem_metadata(infop)) return (0);
if (pf->decoded.g_packet_type != packet_type_message) return(0);
#if defined(PFTEST) || defined(DIGITEST) // TODO: test functionality too, not just syntax.
@ -1486,7 +1446,7 @@ static int filt_i (pfstate_t *pf)
* the past minute, rather than the usual 180 minutes for the addressee.
*/
was_heard = mheard_was_recently_nearby ("source", src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN);
was_heard = mheard_was_recently_nearby ("source", pf->decoded.g_src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN);
if (was_heard) return (0);
@ -1640,24 +1600,29 @@ int main ()
pftest (112, "t/t", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 0);
pftest (113, "t/w", "WM1X>APU25N:@210147z4235.39N/07106.58W_359/000g000t027r000P000p000h89b10234/WX REPORT {UIV32N}<0x0d>", 1);
/* Telemetry metadata is a special case of message. */
/* Telemetry metadata should not be classified as message. */
pftest (114, "t/t", "KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 1);
pftest (115, "t/m", "KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 0);
pftest (116, "t/t", "KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1);
/* Bulletins should not be considered to be messages. Was bug in 1.6. */
pftest (117, "t/m", "A>B::W1AW :test", 1);
pftest (118, "t/m", "A>B::BLN :test", 0);
pftest (119, "t/m", "A>B::NWS :test", 0);
pftest (120, "t/p", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 0);
// https://www.aprs-is.net/WX/
pftest (121, "t/p", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 0);
pftest (122, "t/p", "CWAPID>APRS::SKYCWA :DDHHMMz,ADVISETYPE,zcs{seq#", 0);
pftest (123, "t/p", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0);
pftest (124, "t/n", "CWAPID>APRS::NWS-TTTTT:DDHHMMz,ADVISETYPE,zcs{seq#", 1);
pftest (125, "t/n", "CWAPID>APRS::SKYCWA :DDHHMMz,ADVISETYPE,zcs{seq#", 1);
pftest (126, "t/n", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 1);
//pftest (126, "t/n", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 1);
pftest (127, "t/", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", 0);
pftest (128, "t/c", "S0RCE>DEST:<stationcapabilities", 1);
pftest (129, "t/h", "S0RCE>DEST:<stationcapabilities", 0);
pftest (130, "t/h", "S0RCE>DEST:}thirdpartyheaderwhatever", 1);
pftest (131, "t/c", "S0RCE>DEST:}thirdpartyheaderwhatever", 0);
pftest (130, "t/h", "S0RCE>DEST:}WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
pftest (131, "t/c", "S0RCE>DEST:}WB2OSZ-5>APDW12,DIGI1,DIGI2*,DIGI3,DIGI4:!4237.14NS07120.83W#PHG7140Chelmsford MA", 0);
pftest (140, "r/42.6/-71.3/10", "WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
pftest (141, "r/42.6/-71.3/10", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0);
@ -1712,13 +1677,13 @@ int main ()
pftest (203, "t/w t/w", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", -1);
pftest (204, "r/42.6/-71.3", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", -1);
pftest (220, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (222, "i/30/8/42.6/-71.3/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (223, "i/30/8/42.6/-71.3", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (224, "i/30/8/42.6/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (225, "i/30/8/42.6", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (226, "i/30/8/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (227, "i/30/8", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (210, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (212, "i/30/8/42.6/-71.3/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (213, "i/30/8/42.6/-71.3", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (214, "i/30/8/42.6/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (215, "i/30/8/42.6", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (216, "i/30/8/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (217, "i/30/8", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
// FIXME: behaves differently on Windows and Linux. Why?
// On Windows we have our own version of strsep because it's not in the MS library.
@ -1726,7 +1691,12 @@ int main ()
//pftest (228, "i/30/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (229, "i/30", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (230, "i/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (230, "i/30", "X>X:}WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (231, "i/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
// Besure bulletins and telemetry metadata don't get included.
pftest (234, "i/30", "KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 0);
pftest (235, "i/30", "A>B::BLN :test", 0);
pftest (240, "s/", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
pftest (241, "s/'/O/-/#/_", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
@ -1736,12 +1706,36 @@ int main ()
pftest (245, "s//", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
pftest (246, "s///", "WB2OSZ-5>APDW12:!4237.14N/07120.83WOPHG7140Chelmsford MA", -1);
// Third party header - done properly in 1.7.
// Packet filter t/h is no longer a mutually exclusive packet type.
// Now it is an independent attribute and the encapsulated part is evaluated.
// TODO: to be continued...
pftest (250, "o/home", "A>B:}WB2OSZ>APDW12,WIDE1-1,WIDE2-1:;home *111111z4237.14N/07120.83W-Chelmsford MA", 1);
pftest (251, "t/p", "A>B:}W1WRA-7>TRSY3T,WIDE1-1,WIDE2-1:`c-:l!hK\\>\"4b}=<0x0d>", 1);
pftest (252, "i/180", "A>B:}WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (253, "t/m", "A>B:}WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (254, "r/42.6/-71.3/10", "A>B:}WB2OSZ-5>APDW12,WIDE1-1,WIDE2-1:!4237.14NS07120.83W#PHG7140Chelmsford MA", 1);
pftest (254, "r/42.6/-71.3/10", "A>B:}WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", 0);
pftest (255, "t/h", "KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 0);
pftest (256, "t/h", "A>B:}KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1);
pftest (258, "t/t", "A>B:}KB1GKN-10>APRX27,UNCAN,WIDE1*:T#491,4.9,0.3,25.0,0.0,1.0,00000000", 1);
pftest (259, "t/t", "A>B:}KJ4SNT>APMI04::KJ4SNT :PARM.Vin,Rx1h,Dg1h,Eff1h,Rx10m,O1,O2,O3,O4,I1,I2,I3,I4", 1);
pftest (270, "g/BLN*", "WB2OSZ>APDW17::BLN1xxxxx:bulletin text", 1);
pftest (271, "g/BLN*", "A>B:}WB2OSZ>APDW17::BLN1xxxxx:bulletin text", 1);
pftest (272, "g/BLN*", "A>B:}WB2OSZ>APDW17::W1AW :xxxx", 0);
pftest (273, "g/NWS*", "WB2OSZ>APDW17::NWS-xxxxx:weather bulletin", 1);
pftest (274, "g/NWS*", "A>B:}WB2OSZ>APDW17::NWS-xxxxx:weather bulletin", 1);
pftest (275, "g/NWS*", "A>B:}WB2OSZ>APDW17::W1AW :xxxx", 0);
// TODO: add b/ with 3rd party header.
// TODO: to be continued... directed query ...
if (error_count > 0) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("\nPacket Filtering Test - FAILED!\n");
dw_printf ("\nPacket Filtering Test - FAILED! %d errors\n", error_count);
exit (EXIT_FAILURE);
}
text_color_set (DW_COLOR_REC);